Java ArrayList遍歷foreach與iterator時remove的區別

一、Iterator和foreach的區別

  • 多態差別(foreach底層就是Iterator)
  • Iterator是一個接口類型,他不關心集合或者數組的類型;
  • for和foreach都需要先知道集合的類型,甚至是集合內元素的類型;

1.為啥說foreach底層就是Iterator

編寫的代碼:

反編譯代碼:

二、foreach與iterator時remove的區別

先來看阿裡java開發手冊
但1的時候不會報錯,2的時候就會報錯(java.util.ConcurrentModificationException)

首先來看一下ArrayList中iterator方法的實現:

調用瞭new Itr(),生成Itr類(迭代器)。此時會給Itr的三個參數初始化。

  • cursor代表下一次的索引位置(開始是0)
  • size是集合的大小(2)

拋出異常類
next方法()的時候會檢查checkForComodification是否相等

modCount修改計數(每次add和remove都會+1)expectedModCount期望的最大計數

1.remove操作源碼分析

首先來看一下刪除“2”的情況:
第一次循環:

因為此時的modCount和expectedModCount都為2(因為add瞭兩次所以modCount為2),所以第一次循環中不會拋出異常,拋出異常都是發生在不是第一次循環的情況中。在next方法走完後,foreach循環方法體中的remove方法的if條件判斷不滿足,就結束瞭本次循環。
第二次循環:
第二次循環的hasNext和next方法都是能成功走完的,在這之後會進入到foreach循環方法體中的remove方法中,進行刪除元素。而此時的size-1變為瞭1。在remove方法中的fastRemove方法中,會對modCount+1,也就變為瞭3。

第三次循環:

然後會走入到第三次循環中的hasNext方法中。按照正常的情況下該方法是會返回false的,但因為此時的size已經變為瞭1,而此時的cursor為2(cursor代表下一次的索引位置),所以兩者不等,錯誤地返回瞭true,所以會繼續走入到next方法中的checkForComodification方法中,判斷此時的modCount和expectedModCount是否相等。因為此時的modCount已經變為瞭3,和expectedModCount的值為2不等,所以在此拋出瞭ConcurrentModificationException異常。
再來看一下刪除“1”的時候為什麼不會拋出異常:
 

第一次循環:

同上,此時的modCount和expectedModCount都為2,所以第一次循環中的hasNext和next方法都不會拋異常。在這之後會進入到foreach循環方法體中的remove方法中,進行刪除元素。同上,size-1變為瞭1,而modCount+1變為瞭3。

第二次循環:

在第二次循環的hasNext方法中,此時的cursor為1,而size也是1,兩者相等。所以hasNext方法返回false,就跳出瞭foreach循環,不會走到隨後的next方法中,也就不會拋出異常。

2.源碼步驟

第一次

第①句調用iterator(),

調用瞭new Itr(),生成Itr類(迭代器)。此時會給Itr的三個參數初始化。

此時expectedModCount == modCount == 2(因為list調動瞭add方法,add方法會對modCount實現++操作)
第②句調用下面hasNext()方法,返回下一個要訪問元素的下標cursor,因為是第一次循環,所以cursor為0,size為2 (0 != 2 true)
第③句調用next()方法,foreach循環方法體中的remove方法的if條件判斷不滿足,就結束瞭本次循環

第二次

第②句調用下面hasNext()方法,返回下一個要訪問元素的下標cursor,第二次循環,所以cursor為1,
size還是為2 (1 != 2 true)
第③句調用next()方法,正常取值,取到第一個元素"2";
第④句調用remove()方法,成功給list刪除元素。註意,在調用remove方法的時候,有modCount++。所有此時,modCount3,expectedModCount2,size1

第三次

第②句調用下面hasNext()方法,返回下一個要訪問元素的下標cursor,第二次循環,所以cursor為2,size為1
第③句調用next()方法,註意,在next()方法中第一句話就是調用checkForComodification();由於modCount(3) != expectedModCount(2),所以就拋瞭異常。

3.為啥都是底層都是iterator,為啥foreach會報錯

當循環結束的時候,while (iterator.hasNext()) 會檢查是否有下個元素存在,在remove刪除2完成後,下次進入cursor還是1,size也是1.
foreach的話,刪除remove2之後,下次進入cursor是2,size是1,所以返回false,要走next方法,然後,進行檢查,modCount=3,而expectedModCount=2

三、查看源碼方法

如果查看iterator下的ArrayList

remove也是如此

到此這篇關於Java ArrayList遍歷foreach與iterator時remove的區別的文章就介紹到這瞭,更多相關Java ArrayList遍歷內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: