代碼一:java
public class IteratorTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("hello"); list.add("world"); list.add("java"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String string = (String) iterator.next(); System.out.println(string); if(string.equals("hello")) { list.remove(string); } } } }
結果以下:ide
會拋出ConcurrentModificationException。ui
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:907) at java.util.ArrayList$Itr.next(ArrayList.java:857) at test.IteratorTest.main(IteratorTest.java:17)
代碼二:this
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class IteratorTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("hello"); list.add("world"); list.add("java"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String string = (String) iterator.next(); System.out.println(string); if(string.equals("world")) { list.remove(string); } } } }
結果以下:blog
程序居然正常結束了!element
hello world
代碼三:rem
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class IteratorTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("hello"); list.add("world"); list.add("java"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String string = (String) iterator.next(); System.out.println(string); if(string.equals("java")) { list.remove(string); } } } }
結果以下:源碼
依然是ConcurrentModificationException.string
hello world java Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:907) at java.util.ArrayList$Itr.next(ArrayList.java:857) at test.IteratorTest.main(IteratorTest.java:15)
應該看看源碼,答案就在源碼中。it
ArrayList對iterator()方法的實現是這樣的:
public Iterator<E> iterator() { return new Itr(); }
ArrayList.java裏面有一個內部類:
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
cursor是下一個要被訪問元素的index,lastRet是上一個被訪問的元素。初始的時候cursor是0,lastRet是-1。
第一次調用iterator的next方法,會返回elementData[0]。而後cursor變成1,lastRet變成0。
第二次調用iterator的next方法,會返回elementData[1]。而後cursor變成2,lastRet變成1。
......以此類推,直到hasNext()返回false。而hasNext()返回false的條件是,cursor等於size。
來解釋一下代碼二爲何正常結束:
list裏面放着:"hello", "world", "java"
初始size=3, cursor=0。
第一次循環:hasNext()爲真,next()後,獲得"hello", cursor變爲1;
第二次循環:hasNext()爲真,next()後,獲得"world", cursor變爲2,這時要作刪除操做。刪除操做後size變爲2。
第三次循環:hasNext()爲假,由於cursor = size (都爲2),循環退出,程序正常結束。
代碼一和代碼三會拋出ConcurrentModificationException:
先來看看next方法:
public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; }
第一步會執行checkForComodification():
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
當modCount不等於expectedModCount時,就會拋出這個異常。
expectedModCount在Itr這個內部類中,初始讓它等於modCount。
modCount在AbstractList.java中,初始值是0。當List有add,remove等操做,modCount就會加1。
ArrayList.java中的remove()操做:
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
因此執行list.remove()後,modCount和expectedCount就不同了,下一次next()的時候就會報錯。代碼二是由於hasNext()爲false,躲過了這個報錯。
正確的作法是用iterator.remove():
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class IteratorTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("hello"); list.add("world"); list.add("java"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String string = (String) iterator.next(); System.out.println(string); if(string.equals("java")) { iterator.remove(); } } } }
結果以下:
hello world java