如何在使用for-each循环迭代时修改集合而不出现ConcurrentModificationException异常?

25
如果我在使用 for-each 循环迭代集合时修改了集合,它会抛出 ConcurrentModificationException 异常。有没有什么解决方法?
4个回答

43

使用 Iterator#remove

这是在迭代期间修改集合的唯一安全方式。要了解更多信息,请参见集合接口教程。

如果您还需要在迭代时添加元素,请使用ListIterator


所以我只需创建该集合的迭代器对象,使用hasNext()遍历集合并使用您建议的方法进行删除?谢谢。我也可以添加元素吗? - aps
1
@aps,不,你不能添加元素。是的,要从集合中获取迭代器。 - mre
通过使用listIterator.next()和listIterator.add(),只有在两个“listIterator”是同一个对象时才进行验证,可以在while循环中添加新元素。 - jean

13

一个解决方法是在循环之后保存你所做的更改,然后进行添加/删除操作。

例如:

List<Item> toRemove = new LinkedList<Item>();

for(Item it:items){
    if(remove){
        toRemove.add(it);
    }
}
items.removeAll(toRemove);

谢谢。我之前也考虑过这个问题。但是我有很多集合,所以我必须为每个集合创建大量的冗余toRemove集合。 - aps

3
第二种解决方法是使用一个集合类,该集合类的迭代器不会抛出异常。例如:ConcurrentLinkedQueueConcurrentHashMap等。
这些集合类通过为迭代器提供较弱的一致性模型来避免抛出异常。(当然,您需要了解这些模型,并决定它们是否适合您的应用程序)。
它们通常比非并发集合类慢一些,但如果存在显著的争用,它们比同步集合包装器更快。

请问您能否详细说明一下?我现在正在使用LinkedList,如果改用ConcurrentLinkedList会有任何缺点,例如性能降低或其他方面的问题吗?为什么它不会抛出异常?是列表本身处理修改吗? - aps
你需要阅读Javadocs来了解这个类的属性。(抱歉,我误读了类名...) - Stephen C

2

如果您只想从集合中删除元素,则可以使用 Iterator 而不是 Iterable。

否则,您可以先复制列表而不是迭代原始集合。例如,如果您的集合是一个列表,则可以创建一个新的 ArrayList(originaList) 并遍历它。修改应该在原始列表上进行。

对于您的用例,另一种更好的替代方案是不使用 for-each,而是传统的 for- 循环。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接