从ArrayList中删除元素时出现ConcurrentModificationException

当我运行以下代码时,Java正在抛出ConcurrentModificationException。 Anyidea为什么会这样?

ArrayList list1 = new ArrayList(); list1.add("Hello"); list1.add("World"); list1.add("Good Evening"); for (String s : list1){ list1.remove(2); System.out.println(s); } 

如果你看一下ConcurrentModificationException的文档,你会发现它

当不允许这样的修改时,检测到对象的并发修改的方法可能抛出此exception。

例如, 一个线程通常不允许修改Collection,而另一个线程正在迭代它

请注意,此exception并不总是表示某个对象已被另一个线程同时修改。 如果单个线程发出违反对象合同的一系列方法调用,则该对象可能会抛出此exception。 例如,如果线程在使用失败快速迭代器迭代集合时直接修改集合,则迭代器将抛出此exception

关于此exception的重要一点是,我们无法保证它将始终按照文档中的说明抛出

请注意,无法保证快速失败的行为,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证。 失败快速操作会尽最大努力抛出ConcurrentModificationException

也来自ArrayList文档

这个类的iteratorlistIterator方法返回的iterator快速失败的 :如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出ConcurrentModificationException

(强调我的)

所以你不能操纵Collection的内容(在你的情况下是List),同时通过增强的for循环遍历它,因为你没有通过迭代器来做 – 每个都在内部使用。

要解决它,只需获得自己的Iterator并在循环中使用它。 要从集合中删除元素,请使用此示例中的remove

 Iterator it = list1.iterator(); int i=0; while(it.hasNext()){ String s = it.next(); i++; if (i==2){ it.remove(); System.out.println("removed: "+ s); } } 

迭代时,您无法删除项目。 如果要在迭代时也删除项目,请使用Iterator

但是你要删除一个基于索引的项目,然后只需在循环外移动就可以解决你的问题。

  list1.remove(2); 

形成你可以阅读的文档

当不允许这样的修改时,检测到对象的并发修改的方法可能抛出此exception。 例如,一个线程通常不允许修改Collection,而另一个线程正在迭代它。 通常,在这些情况下,迭代的结果是不确定的。 如果检测到此行为,某些Iterator实现(包括JRE提供的所有通用集合实现的实现)可能会选择抛出此exception。 执行此操作的迭代器称为故障快速迭代器,因为它们快速且干净地失败,而不是在未来的未确定时间冒着任意的,非确定性行为的风险。

此外,假设您可以在每次迭代时删除第2项。 您将最终进入索引超出范围的exception:

  • 在第一次迭代中,您删除项目#2( "Good evening" ),列表大小变为1(项目0 "hello"和项目1 "World"
  • 在下一次迭代中,您将删除列表中实际不存在的第2项。 你的列表的大小确实是2,但是计数器从0开始,因此你最终删除了不存在的东西:这是非确定性行为的一个例子。

修改基础列表后,您无法遍历列表。如果您这样做,它会给您ConcurrentModificationException

要解决此问题,请使用java.util.ListIterator进行列表的迭代。

  ListIterator it = list1.listIterator(); for (String s : list1) { if (it.hasNext()) { String item = it.next(); System.out.println(item); } }