首页 > 解决方案 > 删除 ArrayList 时出现 Java ConcurrentModificationException

问题描述

当 ArrayList 的 Size 为 2 时,我们使用 foreach 循环执行删除操作,它打印最后一个元素,不抛出异常,

例如 :

ArrayList<String> a= new ArrayList<String>(Arrays.asList("abc","xyz"));
    for(String i : a){
        a.remove(i);
    }
// a contains "xyz"

但是当我们将arrayList的大小增加到2以上,并执行操作时,它会显示concurrentModificationException。为什么它会表现出这种行为(它适用于 2 个元素,但不适用于 2 个以上)?

标签: javaconcurrency

解决方案


这是实施的一个怪癖。

仅在尽力而为的基础上抛出异常,不能保证它会被抛出。

要了解原因,您需要了解增强的 for 循环是如何脱糖的:

for (String i : a) {
  a.remove(i);
}

被编译成类似的东西:

Iterator<String> it = a.iterator();
while (it.hasNext()) {
  String i = a.next();
  a.remove(i);
}

接下来,迭代器的实现如下:

int idx = 0;

boolean hasNext() {
  return idx < a.size();
}

Object next() {
  checkForModification(); // throws exception if list has been modified.
  return a.get(idx++);
}

因此,如果列表最初的大小为 2,则循环执行如下:

  • 检查hasNext(): idx == 0,并且0 < 2为真,所以它返回真。
  • 调用next():retrieve i = a.get(0)、incrementidxidx == 1
  • i从中删除a。现在a.size() == 1

循环已执行一次。现在它再次执行:

  • 检查hasNext(): idx == 1,并且1 < 1是假的,所以它返回假。

所以执行停止,不ConcurrentModificationException。该列表仍然包含最初的第二项。

如果列表最初大于 2,则循环第二次执行(因为idx == 1, 和size() == initial size() - 1, 所以idx < initial size() - 1),所以next()将在remove(). 该列表已被修改,因此checkForModification()引发异常。


推荐阅读