为什么 ConcurrentModificationException 只发生在迭代循环中

Why ConcurrentModificationException occurred only at iterate loop

我写了两个示例代码如下:

private static class Person
{
    String name;

    public Person(String name)
    {
        this.name = name;
    }
}
public static void main(String[] args)
{
    List<Person> l = new ArrayList<Person>();
    l.add(new Person("a"));
    l.add(new Person("b"));
    l.add(new Person("c"));
    l.add(new Person("d"));


    for(int i  = 0;i<l.size();i++)
    {
        Person s = l.get(i);
        System.out.println(s.name);
        if(s.name.equals("b"))
            l.remove(i);
    }
    System.out.println("==========================");

    for(Person s : l)
        System.out.println(s.name);
}

当我 运行 示例代码时,在控制台上打印以下结果:

a
b
d
==========================
a
c
d

但是当我按以下迭代模型更改代码时:

  int i  = 0;
  for(Person s : l)
  {
      if(s.name.equals("b"))
          l.remove(i);
      i++;
  }

我得到以下结果:

a
b
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)

例子的方法是:在传统的循环模型中ConcurrentModificationException不会发生在传统的for模型中??

因为foreach循环实际上使用了迭代器。相当于

for(Iterator<Person> it; it.hasNext(); ) {
    Person s = it.next();
    if(s.name.equals("b"))
        l.remove(i);
    i++;
}

而 ArrayList、LinkedList 等标准集合的迭代器在检测到集合在两次调用 next() 之间被修改时会失败。

第一个循环不使用任何迭代器。它直接要求列表获取它的第 n 个元素。这对于 ArrayList 来说很好,但对于 LinkedList 来说非常慢,它必须在每次迭代时遍历列表的一半,使得迭代 O(n^2) 而不是 O(n)。

是的,在您原来的 for 循环中,您通过索引访问值 - 尽管您应该注意您正在跳过对元素 c 的检查(因为到那时was at index 2 is now at index 1).这不可能创建 ConcurrentModificationException,因为在访问之间没有 "context" - 当您修改列表时没有任何内容会失效。

在增强型 for 循环版本中,您使用的是 迭代器,它保留您在集合中所处位置的上下文。调用 l.remove 会使该上下文无效,因此会出现错误。

您可以通过在迭代器本身上调用 remove 来避免它:

for (Iterator<Person> iterator = l.iterator(); iterator.hasNext(); ) {
    Person s = iterator.next();
    if (s.name.equals("b")) {
        iterator.remove();
    }
}

您的第二个示例隐藏了一个内部使用的迭代器,如果存在这样的迭代器,您需要使用具体的迭代器进行删除,而不是使用 "global" 删除方法。在您的第一个示例中,您没有创建迭代器,您只是通过某个索引访问单个元素,这与列表无关。但它确实关心创建的、使用的、活动的迭代器。

List.iterator Iterator.remove