今天同事写了几行相似这样的代码: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检查,以防有变。若是有变,则以异常抛出,因此就出现了上面的异常。