文章目錄
【引言】
經常在迭代集合元素時,會想對集合做修改(add/remove)操作,類似下面這段代碼:
for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) { Integer val = it.next(); if (val == 5) { list.remove(val); }}
運行這段代碼,會拋出異常java.util.ConcurrentModificationException。
【解惑】
(以ArrayList來講解)在ArrayList中,它的修改操作(add/remove)都會對modCount這個欄位+1,modCount可以看作一個版本號碼,每次集合中的元素被修改後,都會+1(即使溢出)。接下來再看看AbsrtactList中iteraor方法
public Iterator<E> iterator() { return new Itr();}
它返回一個內部類,這個類實現了iterator介面,代碼如下:
private class Itr implements Iterator<E> { int cursor = 0; int lastRet = -1; int expectedModCount = modCount; public boolean hasNext() { return cursor != size(); } public E next() { checkForComodification(); try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public void remove() { if (lastRet == -1) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; // 修改expectedModCount 的值 expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
在內部類Itr中,有一個欄位expectedModCount ,初始化時等於modCount,即當我們調用list.iterator()返回迭代器時,該欄位被初始化為等於modCount。在類Itr中next/remove方法都有調用checkForComodification()方法,在該方法中檢測modCount == expectedModCount,如果不相當則拋出異常ConcurrentModificationException。
前面說過,在集合的修改操作(add/remove)中,都對modCount進行了+1。
在看看剛開始提出的那段代碼,在迭代過程中,執行list.remove(val),使得modCount+1,當下一次迴圈時,執行 it.next(),checkForComodification方法發現modCount != expectedModCount,則拋出異常。
【解決辦法】
如果想要在迭代的過程中,執行刪除元素操作怎麼辦?
再來看看內部類Itr的remove()方法,在刪除元素後,有這麼一句expectedModCount = modCount,同步修改expectedModCount 的值。所以,如果需要在使用迭代器迭代時,刪除元素,可以使用迭代器提供的remove方法。對於add操作,則在整個迭代器迭代過程中是不允許的。 其他集合(Map/Set)使用迭代器迭代也是一樣。