并发模式在 ArrayList 中的 foreach 循环内添加时的异常

2022-09-01 14:05:48

我试图在数组列表中使用foreach循环,但是当我使用它时,它会给我错误,但是当我使用正常的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.
}

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

答案 1

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

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

你不能修改一个在循环中,这是语法糖周围的实现细节。您只能在直接使用时安全呼叫。Listfor/eachIterator.remove()Iterator

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

在循环内部调用会修改内容,在后台使用的 调用会看到此情况并引发此异常。.add()for/eachIterator

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

溶液

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

2014 解决方案:

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

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

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


答案 2

在底层,Java 中的 for-each 循环使用 用于遍历集合(有关详细说明,请参阅此文章)。迭代器会抛出一个,如果你在迭代集合时修改它,请参阅这篇文章IteratorConcurrentModificationException