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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9806421/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 07:52:05  来源:igfitidea点击:

ConcurrentModificationException when adding inside a foreach loop in ArrayList

javafor-loopforeacharraylist

提问by hakuna matata

I'm trying to make use of the foreach loop with the arraylist, but when I use it, it gives me error, but when I use normal for loop, it works perfectly, what could be the problem?

我正在尝试将 foreach 循环与 arraylist 一起使用,但是当我使用它时,它给了我错误,但是当我使用普通 for 循环时,它工作得很好,可能是什么问题?

The code is here:

代码在这里:

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.
}

this is the loop that doesnt work, and here is the one that works:

这是不起作用的循环,这是有效的循环:

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.
}

the error I'm getting when using the foreach loop is:

使用 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)

采纳答案by hakuna matata

Java Collection classes are fail-fast which means that if the Collection will be changed while some thread is traversing over it using iterator, the iterator.next()will throw a ConcurrentModificationException.

This situation can come in case of multithreaded as well as single threaded environment. - www.javacodegeeks.com

Java Collection 类是快速失败的,这意味着如果在某个线程使用迭代器遍历它时集合将被更改,iterator.next()则会抛出一个ConcurrentModificationException.

这种情况可能发生在多线程和单线程环境中。- www.javacodegeeks.com

You can't modify a Listin a for/eachloop, which is syntactic sugar around the Iteratoras an implementation detail. You can only safely call .remove()when using the Iteratordirectly.

您不能Listfor/each循环中修改 a ,这是围绕Iterator作为实现细节的语法糖。只有.remove()Iterator直接使用时才能安全地调用。

Note that Iterator.remove is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress. - Java Collections Tutorial

请注意, Iterator.remove 是在迭代期间修改集合的唯一安全方法;如果在迭代过程中以任何其他方式修改了基础集合,则行为是未指定的。- Java 集合教程

Calling .add()inside the for/eachloop modifies the contents, and the Iteratorthat is used behind the scenes sees this and throws this exception.

.add()for/each循环内部调用会修改内容,Iterator幕后使用的 会看到这个并抛出这个异常。

A more subtle concern is the that the second way you list, the .size()is increasing every time you .add()so you will end up processing all the things you .add(), this could possibly cause an endless loop depending on what the input data is. I am not sure if this is what you desire.

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

Solution

解决方案

I would create another ArrayListand .add()to it all the new things, and then after the loop, use .addAll()on the original ArrayListto combine the two lists together. This will make things explicit in what you are trying to do, that is unless your intention is process all the newly added things as you add them.

我会创建另一个ArrayList.add()添加所有新内容,然后在循环之后,使用.addAll()原始ArrayList列表将两个列表组合在一起。这将使您尝试做的事情变得明确,除非您的意图是在添加它们时处理所有新添加的东西。

2014 Solution:

2014年解决方案:

Always use Immutablecollections classes and build new Immutablecollection classes instead of trying to modify a single shared one. This is basically what my 2012 answer says but I wanted to make it more explicit.

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

Guava supports this very well, use ImmutableList.copyOf()to pass around data.

Guava 很好地支持了这一点,用于ImmutableList.copyOf()传递数据。

Use Iterables.filter()to filter out stuff into a new ImmutableList, no shared mutable state, means no concurrency problems!

使用Iterables.filter()过滤掉的东西到一个新的ImmutableList,没有共享的可变状态,意味着没有并发问题!

回答by óscar López

Under the hood, the for-each loop in Java uses an Iteratorfor traversing the collection (see this articlefor a detailed explanation.) And the Iterator will throw a ConcurrentModificationExceptionif you modify the collection while iterating over it, see this post.

在底层,Java 中的 for-each 循环使用 anIterator来遍历集合(有关详细说明,请参阅这篇文章。)ConcurrentModificationException如果您在迭代时修改集合,迭代器将抛出 a ,请参阅这篇文章

回答by Paul Rubel

The problem is that you're doing the R.add() in the first line of the loop.

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

In the first situation you have an iterator open to the arraylist. When you do an add and then try to iterate again the iterator notices that the data structure has changed underneath you.

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

In the case of the for look you're just getting a new element each time and don't have the concurrent modification issue, though the size is changing as you add more elements.

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

To fix the problem you probably want to add to a temporary location and add that after the loop or make a copy of the initial data and add to the original.

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