public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("A"); list.add("B"); list.add("C"); Iterator<String> iterator = list.iterator(); while(iterator.hasNext()) { // list.remove("C"); System.out.println(iterator.next()); } }
以上代碼運行都沒有問題,就是取出List數據,而後打印。
java
可是要是把註釋打開那就會報錯,java.util.ConcurrentModificationException,這裏是爲何呢?那就來討論一下迭代器的原理吧。數據庫
經過list.iterator查找發現上面截圖,裏面建立了Itr對象,下面就看看這個對象的具體內容。數據結構
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 = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
Itr是AbstractList的一個內部類。this
方法hasNext:比對cursor(下一個要訪問的元素的索引)與當前長度。spa
方法next:根據cursor值獲取元素信息。code
方法remove:刪除lastRet(上一個訪問元素的索引)值獲取元素信息。對象
方法checkForComodification:檢驗(expectedModCount與modCount是否相等),前面的錯誤就是這裏產生的。索引
public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work }
ArrayList的方法remove只是修改了參數modCount。element
也就是在使用迭代器遍歷的時候,就不能使用List自身的方法(add/remove)去修改原來的List的結構,要不會報錯,而使用迭代器自身的方法(remove)是不會報錯的,這又是爲何呢?
rem
上圖中的紅框中明顯發現,是把下一個訪問的元素下標及上一個訪問元素下標給修改了。
總結
JAVA的迭代器更像是數據庫中的一個鏡像,當你使用原始List修改了原始數據結構時,它會報錯,必須使用迭代器的方法remove才能夠不會報錯。