1. 程式人生 > >為什麼ArrayList在使用迭代器迭代元素時不能使用List.remove()刪元素,而是使用Iterator.remove()刪元素

為什麼ArrayList在使用迭代器迭代元素時不能使用List.remove()刪元素,而是使用Iterator.remove()刪元素

其實,我相信有一定經驗的都已經知道了,把標題的“為什麼”去掉讀一遍,但是具體是為什麼?今天本人跟進原始碼(jdk7)探了個究竟。注:這篇文章只介紹使用list.remove()丟擲ConcurrentModificationException的原因,其它引數及異常不作介紹。

直入主題,首先我們來看下面這段出問題的程式碼


下面是執行結果


使用iterator.remove()是沒有問題的,此處就不粘圖了

由執行結果可知,拋異常的地方出現在iterator.next(),而且已經遍歷完了“bvBody”,當嘗試獲取“cvBody”時拋了異常。根據異常第一行可知問題出在ArrayList的內部類Itr(也就是程式碼中的iterator)的checkForComodification()方法,下面是原始碼


簡單明瞭,當modCount != expectdModCount時,丟擲併發修改異常(記住這個點)。那麼,modCount和expectedModCount分別是什麼?modCount在ArrayList中的定義如下,簡單理解就是記錄改變了ArrayList的size(增刪操作)的次數



expectedModCount在ArrayList的Itr中的定義如下


當ArrayList呼叫iterator()方法,初始化iterator時,將list的modCount值賦給expectedModCount,此時此刻,如果後續expectedModCount和modCount的值不變,或者同步改變保持相等,iterator.next()是不會拋併發修改異常的。既然拋了,它們肯定沒有同步修改,直接跟進list.remove()到ArrayList.remove()方法中檢視,原始碼如下


跟進fastRemove()原始碼如下


System.arraycopy()效果如下圖


回到剛才說的list.remove(),方法中modCount自增了,而expectedModCount是沒有修改的,因此,在下一次iterator呼叫next()時,將丟擲ConcurrentModificationException。

問題似乎已經弄明白了,不,我們再看看iterator.remove()方法為什麼是OK的。如下是其原始碼


首先呼叫外部類ArrayList的remove()方法,即前面圖片中所示,其中修改了modCount。而此處,同時將expectedModCount置為modCount的值,它倆的值再次同步了,所以,iterator的remove()是OK的。

關於可能其它的地方也修改了modCount或expectedModCount的值,在上面程式碼中,已經確認,除了本文所述的地方存在修改以及list.add()修改了modCount,從迭代開始,是沒有其它修改的。

結論:

在使用ArrayList過程中,當使用iterator迭代獲取元素並需要移除元素時,需使用iterator的remove()方法移除元素。如果使用list.remove(),將在iterator下一次呼叫next()時丟擲ConcurrentModificationException。如果中途呼叫list.add(),同樣會修改modCount,導致丟擲異常。