/**
* 解讀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();
}
}