今天同事寫了幾行相似這樣的代碼:java
public static void main(String args[]) { List<String> famous = new ArrayList<String>(); famous.add("liudehua"); famous.add("madehua"); famous.add("liushishi"); famous.add("tangwei"); for (String s : famous) { if (s.equals("madehua")) { famous.remove(s); } } }
運行出異常:ide
Exception in thread "main" java.util.ConcurrentModificationExceptionthis
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)spa
at java.util.AbstractList$Itr.next(AbstractList.java:343)element
at com.bes.Test.main(Test.java:15)rem
Java新手最容易犯的錯誤,對JAVA集合進行遍歷刪除時務必要用迭代器。切記。get
其實對於如上for循環,運行過程當中仍是轉換成了以下代碼:同步
for(Iterator<String> it = famous.iterator();it.hasNext();){ String s = it.next(); if(s.equals("madehua")){ famous.remove(s); } }
仍然採用的是迭代器,但刪除操做卻用了錯誤的方法。如將famous.remove(s)改爲it.remove()it
則運行正常,結果也無誤。io
固然若是改爲:
for (int i = 0; i < famous.size(); i++) { String s = famous.get(i); if (s.equals("madehua")) { famous.remove(s); } }
這種方法,也是能夠完成功能,但通常也不這麼寫。
爲何用了迭代碼器就不能採用famous.remove(s)操做? 這種由於ArrayList與Iterator混合使用時會致使各自的狀態出現不同,最終出現異常。
咱們看一下ArrayList中的Iterator實現:
private class Itr implements Iterator<E> { /** * Index of element to be returned by subsequent call to next. */ int cursor = 0; /** * Index of element returned by most recent call to next or * previous. Reset to -1 if this element is deleted by a call * to remove. */ int lastRet = -1; /** * The modCount value that the iterator believes that the backing * List should have. If this expectation is violated, the iterator * has detected concurrent modification. */ 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 = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
基本上ArrayList採用size屬性來維護自已的狀態,而Iterator採用cursor來來維護自已的狀態。
當size出現變化時,cursor並不必定可以獲得同步,除非這種變化是Iterator主動致使的。
從上面的代碼能夠看到當Iterator.remove方法致使ArrayList列表發生變化時,他會更新cursor來同步這一變化。但其餘方式致使的ArrayList變化,Iterator是沒法感知的。ArrayList天然也不會主動通知Iterator們,那將是一個繁重的工做。Iterator到底仍是作了努力:爲了防止狀態不一致可能引起的沒法設想的後果,Iterator會常常作checkForComodification檢查,以防有變。若是有變,則以異常拋出,因此就出現了上面的異常。