在使用迭代器对Vector、ArrayList进行迭代的时候,如果在迭代过程中对其增、删操作,就会抛出java.util.ConcurrentModificationException。
示例:
public class Practice { public static void main(String[] args) { List<Integer> list=new ArrayList<Integer>(); list.add(1); Iterator<Integer> iterator=list.iterator(); while (iterator.hasNext()) { int i=iterator.next(); list.remove(i); } } }
异常信息:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at test.Practice.main(Practice.java:17)
原因分析:
1、代码结构
ArrayList 继承了 AbstractList,Itr是AbstractList中的内部类,Itr 继承了 Iterator。
其中,ArrayList中的iterator();方法继承自AbstractList:
public Iterator<E> iterator() { return new Itr(); }
Itr的原码:
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 { int i = cursor; E next = get(i); lastRet = i; cursor = i + 1; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public void remove() { if (lastRet < 0) 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中:
cursor:表示下一个要访问的元素的索引
lastRet:表示上一个访问的元素的索引
expectedModCount:ArrayList的预期修改次数,初始值为modCount。
modCount是AbstractList类中的一个成员变量,为增、删次数。2、异常原因
Iterator的next()的实现过程:
public E next() { checkForComodification(); try { int i = cursor; E next = get(i); lastRet = i; cursor = i + 1; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } }
在调用Iterator的next()方法时,会调用checkForComodification();
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
在checkForComodification()中,会检查list的实际修改次数(modCount)与内部类Itr的预期修改次数(expectedModCount)是否相等,不相等时,抛出java.util.ConcurrentModificationException。而list中的remove()方法并不会改变expectedModCount,只会改变modCount。
ArrayList中的remove()方法:
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; // clear to let GC do its work }
iterator中的remove(),会同步expectedModCount 、modCount;
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } }
3、总结
ArrayList中的迭代器在获取集合中的下个元素时,会用迭代器中保存的集合修改次数和集合本身保存的修改次数进行对比(expectedModCount、modCount),目的在于保证当前迭代集合的完整性。
使用迭代器Iterator对集合ArrayList进行遍历时,在遍历过程中对集合元素进行增、删操作时,使用迭代器的remove方法,防止出现ConcurrentModificationException。