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

时间:2015-06-16 14:04:02

标签: java concurrentmodification

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

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不是传统的模型发生的吗?

3 个答案:

答案 0 :(得分:1)

因为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)。

答案 1 :(得分:1)

是的,在您原来的for循环中,您正在按索引访问该值 - 尽管您应该注意到您正在跳过元素c的检查(因为那时现在位于索引1处。这不会创建ConcurrentModificationException,因为访问之间没有“上下文” - 修改列表时没有任何内容可以失效。

在增强型for循环版本中,您使用的是迭代器,它保留了您在集合中的位置的上下文。调用l.remove会使该上下文无效,从而导致错误。

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

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

答案 2 :(得分:0)

你的第二个例子隐藏了一个内部使用的迭代器,如果这样的迭代到位,你需要使用具体的迭代器删除,而不是使用“全局”删除方法。在您的第一个示例中,您不创建迭代器,您只是通过某个索引访问单个元素,这不是列表所关心的。但它确实关心创建的,使用过的,活跃的迭代器。

List.iterator Iterator.remove