簡介
或許有很多小夥伴都嘗試過如下的程式碼:
ArrayList<Object> list = ...;
for (Object object : list) {
if (條件成立) {
list.remove(object);
}
}
然後會發現丟擲java.util.ConcurrentModificationException
異常,這是一個併發異常。那麼這個到底是什麼情況?首先需要介紹一下增強for迴圈
增強for迴圈
增強for迴圈是Java1.5後,Collection實現了Iterator介面後出現的。增強for迴圈的程式碼如下
for (Object object : list) {
// 操作
}
其實增強for迴圈就是使用Iterator迭代器進行迭代的,增強for迴圈就變成下面這樣:
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
// 操作
}
那麼為什麼在增強for迴圈中呼叫list.remove(object)
會出事呢?那麼我們們看看ArrayList下的 Iterator的實現類: Itr類
Itr子類
Itr子類是Iterator的實現類,屬於ArrayList私有的區域性內部類。我擷取了Itr類的部分程式碼,如下:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
elementData 是ArrayList存放元素的陣列,上面程式碼沒有貼出來。
size 是elementData實際存放的容量大小
modCount 記錄elementData容量的修改次數
expectedModCount 記錄例項化迭代器Itr時,elementData容量的修改次數
注意!:在迭代器中,當執行next
方法的時候,會去呼叫checkForComodification
方法,判斷elementData 的容量是否被修改過。
然後來看看ArrayList的remove(object)方法,擷取部分程式碼如下:
public boolean remove(Object o) {
for (int index = 0; index < size; index++)
if (找到目標元素) {
fastRemove(index);
return true;
}
return false;
}
private void fastRemove(int index) {
modCount++;
// 移除操作
}
可以發現,呼叫remove(object)方法時呼叫了fastRemove方法,在fastRemove方法中執行modCount++
!
現在把文章開頭的程式碼拷下來,再來分析一次:
ArrayList<Object> list = ...;
for (Object object : list) {
if (條件成立) {
list.remove(object);
}
}
當執行了list.remove
時,執行modCount++
。此時迭代器再往下進行迭代,執行了next方法,發現 modCount != expectedModCount
,那麼則丟擲java.util.ConcurrentModificationException
異常。 之所以Iterator認為是一個併發異常。是因為你不在迭代器裡操作,而是在迭代器外面進行remove操作呀!
難道沒有其他解決方案嗎?有滴。
解決方案
那麼就是使用Itr的 remove方法。Itr子類重寫了 remove 方法,這裡部分程式碼:
public void remove() {
...
try {
ArrayList.this.remove(需要刪除元素的索引);
...
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
其實很簡單,就是remove後,把 expectedModCount 同步一下 modCount 的值,這就解決了。完整程式碼如下:
ArrayList<Object> list = ...;
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
總結
本來我還不知道增強for迴圈是呼叫Iterator進行迭代的,要不是我debug了一波,我還不知道吶。還是小有收貨。
個人部落格網址: https://colablog.cn/
如果我的文章幫助到您,可以關注我的微信公眾號,第一時間分享文章給您