Java ConcurrentModificationException在ArrayList中的foreach循环内添加时

Java ConcurrentModificationException在ArrayList中的foreach循环内添加时,java,for-loop,foreach,arraylist,Java,For Loop,Foreach,Arraylist,我试着在arraylist中使用foreach循环,但是当我使用它时,它会给我错误,但是当我使用normal for循环时,它工作得很好,会有什么问题 代码如下: for (Pair p2 : R) { if ((p2.getFirstElm() == p.getSecondElm()) && (p2.getFirstElm() != p2.getSecondElm())) R.add(new Pair (p.getFirstElm(), p2.getS

我试着在arraylist中使用foreach循环,但是当我使用它时,它会给我错误,但是当我使用normal for循环时,它工作得很好,会有什么问题

代码如下:

for (Pair p2 : R) {
    if ((p2.getFirstElm() == p.getSecondElm()) && (p2.getFirstElm() != p2.getSecondElm())) 
        R.add(new Pair (p.getFirstElm(), p2.getSecondElm()));
    else if ((p2.getSecondElm() == p.getFirstElm()) && (p2.getFirstElm() != p2.getSecondElm())) 
        R.add(new Pair (p2.getFirstElm(), p.getSecondElm()));

    // else
    // There are no transitive pairs in R.
}
这是一个不起作用的循环,下面是一个起作用的循环:

for (int i = 0; i < R.size(); i++) {
    if ((R.get(i).getFirstElm() == p.getSecondElm()) && (R.get(i).getFirstElm() != R.get(i).getSecondElm())) 
        R.add(new Pair (p.getFirstElm(), R.get(i).getSecondElm()));
    else if ((R.get(i).getSecondElm() == p.getFirstElm()) && (R.get(i).getFirstElm() != R.get(i).getSecondElm())) 
        R.add(new Pair (R.get(i).getFirstElm(), p.getSecondElm()));
    //else
    //  There are no transitive pairs in R.
}
Java集合类是快速失败的,这意味着 当某个线程遍历集合时,该集合将被更改 使用迭代器,
iterator.next()
将抛出一个
ConcurrentModificationException

这种情况可能发生在多线程和单线程的情况下 线程环境

不能在
for/each
循环中修改
列表
,这是围绕
迭代器
的语法糖,是实现细节。直接使用
迭代器时,只能安全地调用
.remove()

请注意,Iterator.remove是修改集合的唯一安全方法 迭代过程中;如果基础的 当迭代进行时,集合将以任何其他方式修改 进步

for/each
循环中调用
.add()
会修改内容,而在后台使用的
迭代器会看到这一点并引发此异常

一个更微妙的问题是,第二种列出方式,
.size()
在每次
.add()
时都会增加,因此您将最终处理所有
.add()
的内容,这可能会导致无休止的循环,具体取决于输入数据的内容。我不确定这是否是你想要的

解决方案

我将创建另一个
ArrayList
.add()
将所有新事物添加到其中,然后在循环之后,在原始
ArrayList
上使用
.addAll()
将两个列表组合在一起。这将使事情在你试图做的事情中变得明确,也就是说,除非你的意图是在你添加时处理所有新添加的事情

2014年解决方案: 始终使用
Immutable
集合类并构建新的
Immutable
集合类,而不是尝试修改单个共享类。这基本上就是我2012年的答案,但我想让它更明确

Guava非常支持这一点,使用
ImmutableList.copyOf()
传递数据


使用
Iterables.filter()
将内容过滤到一个新的
不可变列表
,没有共享的可变状态,意味着没有并发问题

在引擎盖下,Java中的for-each循环使用一个
迭代器来遍历集合(详细说明见此)。如果您在对集合进行迭代时修改了该集合,迭代器将抛出一个
ConcurrentModificationException
,请参见此。

问题在于您正在执行R.add()在循环的第一行

在第一种情况下,您有一个对arraylist打开的迭代器。当您执行添加操作,然后再次尝试迭代时,迭代器会注意到您下面的数据结构已经更改

在for外观的情况下,您每次只获得一个新元素,而不存在并发修改问题,尽管随着添加更多元素,大小会发生变化

要解决此问题,您可能需要添加到临时位置并将其添加到循环后,或者复制初始数据并添加到原始数据。

您的语句为false,“您无法修改for/each循环中的列表”。您可以修改foreach循环中的列表。唯一需要注意的是,在修改列表后,必须打破循环。
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
    at java.util.AbstractList$Itr.next(Unknown Source)  
    at set.problem.fourth.PoSet.makeTransitive(PoSet.java:145)  
    at set.problem.fourth.PoSet.addToR(PoSet.java:87)
    at set.problem.fourth.PoSetDriver.typicalTesting(PoSetDriver.java:35)
    at set.problem.fourth.PoSetDriver.main(PoSetDriver.java:13)