设想你有一个简单的需求:在一个ArrayList中的某个位置插入一个新的元素,于是你写下了如下代码
ArrayListlist = new ArrayList<>(); list.add("aa"); list.add("bb"); list.add("cc"); list.add("dd"); list.add("ee"); //在"cc"之后添加一个字符串"kk" Iterator it = list.iterator(); while(it.hasNext()){ if("cc".equals(it.next())){ list.add("kk"); // 插入一个新的元素 } }
发现报错:
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 org.zyf.Main.main(Main.java:20)
说明在调用it.next()的时候抛出了ConcurrentModificationException异常.
出错原因:ArrayList类继承了抽象类AbstractList,所以ArrayList拥有一个成员变量modCount,记录了对ArrayList的操作次数:
// AbstractList.java protected transient int modCount = 0;
每次对 ArrayList 的 add 和 remove 方法的成功调用,都会使 modCount 自增。
阅读源码可以发现,在对 ArrayList 使用迭代器进行迭代的时候,使用的其实是实现了 Iterater 接口的 ArrayList 内部类 Itr,
// ArrayList.java public Iteratoriterator() { return new Itr(); } ... private class Itr implements Iterator { ... }
迭代时,我们调用 Itr.next(),而这个方法会调用 checkForComodification() 方法:
// ArrayList.Itr
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() 做了什么:
// ArrayList.Itr
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这个方法检查了 modCount 和 expectedModCount 的一致性,如果不一致,则抛出 ConcurrentModificationException 异常,这里就是报错的源头。
而 Itr 进行初始化的时候,其成员变量 expectedModCount 正是被初始化为了modCount 的值:
// ArrayList.java private class Itr implements Iterator{ int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; ... }
这说明在创建迭代器时expectedModCount和modCount就是一致的。
但是!在创建迭代器之后,任何对ArrayList的直接修改(绕过迭代器对其进行修改),都不会影响 expectedModCount!
文章开头代码中直接使用List.add()加入新元素,只会修改modCount的值,expectedModCount 并不会发生变化,所以无法通过一致性检查,导致了异常。
解决办法:事情让一个“人”做 —— 引入新的迭代器:ListIterator,该迭代器提供了安全的add方法,可使对ArrayList的修改通过一致性检查。
// ArrayList.ListItr,实现了ListIterator接口
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount; // 更新expectedModCount
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
迭代和添加操作都是靠ListIterator来完成的:
ArrayListtip:list = new ArrayList<>(); list.add("aa"); list.add("bb"); list.add("cc"); list.add("dd"); list.add("ee"); //在"cc"之后添加一个字符串"kk" ListIterator it = list.listIterator(); while(it.hasNext()){ if("cc".equals(it.next())){ it.add("kk"); }
如果是想要迭代时删除元素,不必使用ListIterater,调用Iterator的remove即可
ArrayListlist = new ArrayList<>(); list.add("aa"); list.add("bb"); list.add("cc"); list.add("dd"); list.add("ee"); // 移除"cc" Iterator it = list.iterator(); while(it.hasNext()){ if("cc".equals(it.next())){ it.remove(); } }



