首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在ArrayList中的foreach循环内添加时的ConcurrentModificationException

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

Stack Overflow用户
提问于 2012-03-21 22:17:53
回答 3查看 41.5K关注 0票数 24

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

代码如下:

代码语言:javascript
复制
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.
}

这是一个不起作用的循环,下面是一个起作用的:

代码语言:javascript
复制
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.
}

在使用foreach循环时,我得到的错误是:

代码语言:javascript
复制
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)
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-03-21 22:19:16

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

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

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

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

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

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

解决方案

我将创建另一个ArrayList并对所有新内容执行.add(),然后在循环之后,在原始ArrayList上使用.addAll()将两个列表合并在一起。这将使你正在尝试做的事情变得清晰,除非你的意图是在添加时处理所有新添加的东西。

2014年解决方案:

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

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

使用Iterables.filter()将内容过滤到新的ImmutableList中,没有共享的可变状态,意味着没有并发问题!

票数 43
EN

Stack Overflow用户

发布于 2012-03-21 22:21:48

在幕后,Java语言中的for-each循环使用一个Iterator来遍历集合(有关详细解释,请参阅此article )。如果在迭代时修改集合,迭代器将抛出ConcurrentModificationException,请参见此post

票数 4
EN

Stack Overflow用户

发布于 2012-03-21 22:23:33

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

在第一种情况下,您打开了一个指向数组列表的迭代器。当您执行add操作,然后尝试再次迭代时,迭代器会注意到您下面的数据结构发生了变化。

在for look的情况下,每次只会得到一个新的元素,没有并发修改的问题,尽管大小会随着添加更多的元素而变化。

要解决这个问题,您可能需要添加到一个临时位置并将其添加到循环之后,或者制作初始数据的副本并添加到原始数据。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9806421

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档