解讀ArrayList集合中,for循環遍歷和迭代器遍歷的不同

/**
 * 解讀ArrayList集合中,for循環遍歷和迭代器遍歷的不同
 *
 * @author MoCha
 * @date 2020/4/3
 */
public class Demo {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(1, 23, 4, 5));

        // An optimized version of AbstractList.ListItr
        // 從源碼可以看出java.util.ArrayList.ListItr是進行過優化的版本
        // 內部繼承了java.util.ArrayList.Itr,同時實現了java.util.ListIterator接口
        // java.util.ArrayList.ListItr提供更多方法給我們操作集合,比如hasPrevious, add方法
        // 所以如果想要在遍歷的時候,爲ArrayList添加元素,可以使用listIterator()
        ListIterator<Integer> integerListIterator = arrayList.listIterator();
        while (integerListIterator.hasNext()) {
            // integerListIterator.add(1);
            // 但凡使用remove操作,都需要先調用next方法,因爲remove操作方法是ListIterator繼承自java.util.ArrayList.Itr
            // 如果不先調用next方法,那麼lastRet始終爲-1,在調用remove方法時,會拋出IllegalStateException();
            integerListIterator.next();
            integerListIterator.remove();
            if (arrayList.size() < 2) {
                break;
            }
        }

        System.out.println(arrayList);
        // for循環遍歷,實際上用到了next方法,否則,for循環左邊的值怎麼取的呢
        // 而next過程中會判斷modCount是否有改變,所以不能在for循環中調用ArrayList的add, remove等方法
        // 這些方法,都會使得集合的modCount值發生變化,從而快速失敗
        // 由debug模式下,調試以下代碼可以知道,當第一次進入該循環時,是可以對ArrayList進行添加操作的,
        // 不過需要注意的是此時ArrayList的modCount已經發生變化,所以第二次,也就是調用next()方法的時候,檢查checkForComodification時出現異常
        // 同理,remove也會

        // 如果說爲什麼要用iterator迭代器進行遍歷,而不是用for循環來操作
        // 最主要的原因是,你無法在for循環裏面,對集合的元素進行改動,否則會報異常
        // 而使用迭代器的方式,你可以調用ArrayList的listIterator(),而這個java.util.ArrayList.ListItr提供了add, remove方法
        for (Integer integer : arrayList) {
            // 從源代碼可以看出,java.util.ArrayList.add(E)操作是會Increments modCount
            // ensureCapacityInternal(size + 1);  // Increments modCount!!
            arrayList.add(1);
        }
        System.out.println(arrayList);
    }
}

運行結果

[5]
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at top.yangzefeng.movie.Demo.main(Demo.java:43)

總結:

  • for循環中,無法對ArrayList進行改動,否則會出現ConcurrentModificationException

  • 如果僅僅只是對ArrayList進行讀取遍歷,使用forEach會更加簡潔

  • 如果想要在迭代時對ArrayList進行改動,推薦使用listIterator()方法,該方法返回的java.util.ArrayList.ListItr,繼承了ArrayList內部的Itr,實現了ListIterator接口,提供瞭如hasPrevious, add方法,更加方便我們在遍歷時操作ArrayList

  • 需要注意的是,在迭代時,不能上來直接就調用remove方法,因爲此時lastRet始終爲-1,在調用remove方法時,會拋出IllegalStateException();

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

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章