ArrayList集合爲什麼不能使用foreach增加、刪除、修改元素???

先看一個代碼:

public class ArrayListForeach {
    public static void main(String[] args) {
        removeListElement1();  // 沒有問題
        removeListElement2();  // ConcurrentModificationException異常
    }

    public static void removeListElement1() {
        List<String> arrayList = new ArrayList<String>();
        arrayList.add("1");
        arrayList.add("2");
        for (String s : arrayList) {
            if ("1".equals(s)) {
                arrayList.remove(s);
            }
        }
        arrayList.stream().forEach(System.out::println);
    }

    public static void removeListElement2() {
        List<String> arrayList = new ArrayList<String>();
        arrayList.add("2");
        arrayList.add("1");
        for (String s : arrayList) {
            if ("1".equals(s)) {
                arrayList.remove(s);
            }
        }
        arrayList.stream().forEach(System.out::println);
    }
}

 

public static void removeListElement4() {
        List<String> arrayList = new ArrayList<String>();
        arrayList.add("1");
        arrayList.add("2");
        arrayList.add("3");

        for (String s : arrayList) {
            if ("1".equals(s)) {
                arrayList.remove(s);
            }
        }
        arrayList.stream().forEach(System.out::println);
    }

 

當有三個元素時候,刪除還是會拋異常,

爲什麼會這樣呢?

foreach的本質就是使用的迭代器Iterator,所有的Collection集合類都會實現Iterable接口。

看一下Iterator代碼,

public Iterator<E> iterator() {
    return new Itr();
}

再進到new itr裏面看看: 

private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;

        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;

        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size();
        }

        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

這就是源碼部分了,迭代器的本質就是先調用hasNext()判斷是否有下一個元素存在,然後調用next()取到下一個元素。

再看看上面示例代碼:

上面arraylist1爲什麼能remove成功呢,其實它只循環了一次,所以成功了。

因爲它在remove元素1之後,它的size - 1變成1,然後Itr內部的cursor變量由0變成1
此時1 = 1,循環結束,所以成功了。

arraylist2爲什麼remove失敗呢,因爲他在循環第二次的時候,也remove成功了,但是第三次判斷next的時候cursor的值爲2導致不等於現在的size 1,所以執行了next方法,最重要的來了,之前remove的操作導致ArrayList的modCount值加1,然後Itr類中的expectedModCount保持不變,所以會拋出異常。所以會看到上面的異常信息。

list的remove()方法:

所以,要對List進行刪除的時候,可以用下面的迭代器方式:

public static void removeListElement3(){
        List<String> arrayList = new ArrayList<String>();
        arrayList.add("2");
        arrayList.add("1");
        Iterator<String> iterator = arrayList.iterator();
        while(iterator.hasNext()){
            String item = iterator.next();
            if("1".equals(item)){
                iterator.remove();
            }
        }
        arrayList.stream().forEach(System.out::println);
    }

迭代器自己的remove()方法,每次讓expectedModCount = modCount,當然在併發情況下,還得加索,不然還是會拋異常。

public static void removeListElement5() {
        List<String> arrayList = new ArrayList<String>();
        arrayList.add("2");
        arrayList.add("1");
//        for (String s : arrayList) {
//            if ("1".equals(s)) {
//                arrayList.remove(s);
//            }
//        }
        for (int i =0;i<arrayList.size();i++){
            if ("1".equals(arrayList.get(i))) {
                arrayList.remove(arrayList.get(i));
            }
        }
        arrayList.stream().forEach(System.out::println);
    }

 這種普通for循環也沒有什麼問題,如果有不對的地方,歡迎大家指點。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章