java.util.ConcurrentModificationException 异常详解

文章目录
  1. 1. 问题复现
  2. 2. 原因分析
  3. 3. 解决方法
  4. 4. 参考

环境:JDK 1.8.0

使用 iterator 遍历集合的同时对集合进行修改会报错 java.util.ConcurrentModificationException

问题复现

抛异常代码

@Test
public void test02() {
List<Integer> list = new ArrayList<>();
for (int i = 1; i < 11; i++) {
list.add(i);
}

Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer next = it.next();
if (next == 5) {
list.remove(next);
}
}
}

异常信息

java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)

原因分析

使用 Iterator 遍历 ArrayList, 抛出异常的是 iterator.next(),查看 ArrayList 的 Iterator 实现源码

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];
}

在 next() 方法中有一个 checkForComodification() 方法

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

可以看到 checkForComodification 方法是用来判断集合的修改次数是否合法。

modCount 字段用于记录集合被修改的次数,ArrayList 增删改 (add, remove, set) 时都会自增 modCount 属性。

在创建 Iterator 的时候会将 modCount 赋值给 expectedModCount,同样记录当前集合修改的次数,初始化为集合的 modCount 值。

抛异常代码中 ArrayList 添加了 10 次所以 modCount = 10;创建 Iterator 时候 expectedModCount = 10;遍历到 next == 5 时执行了 list.remove(next),此时 modCount = 11, expectedModCount = 10;

在执行 next 方法时,判断 modCount != expectedModCount ,导致抛出异常 java.util.ConcurrentModificationException。

为什么要这么做呢?引用一段解释

Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。

  所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

解决方法

使用 Iterator 本身的方法 remove() 来删除对象

正确代码

@Test
public void test02() {
List<Integer> list = new ArrayList<>();
for (int i = 1; i < 11; i++) {
list.add(i);
}

Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer next = it.next();
if (next == 5) {
// 使用 Iterator 本身的方法 remove() 来删除对象
it.remove();
}
}
}

参考

https://www.cnblogs.com/xujian2014/p/5846128.html

https://www.cnblogs.com/snowater/p/8024776.html

https://www.iteye.com/blog/lz12366-675016