AFAIK,有两种方法:
- 遍历集合的副本
- 使用实际集合的迭代器
例如,
List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
// modify actual fooList
}
和
Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
// modify actual fooList using itr.remove()
}
是否有任何理由更喜欢一种方法而不是另一种方法(例如,出于可读性的简单原因而更喜欢第一种方法)?
原文由 user1329572 发布,翻译遵循 CC BY-SA 4.0 许可协议
让我举几个例子和一些替代方案来避免
ConcurrentModificationException
。假设我们有以下藏书
收集和删除
第一种技术包括收集我们要删除的所有对象(例如使用增强的 for 循环),并在完成迭代后删除所有找到的对象。
这是假设您要执行的操作是“删除”。
如果你想“添加”这种方法也可以,但我假设你会迭代一个不同的集合来确定你想要添加到第二个集合的
addAll
,然后在结尾。使用列表迭代器
如果您正在使用列表,另一种技术包括使用
ListIterator
它支持在迭代过程中删除和添加项目。同样,我在上面的示例中使用了“删除”方法,这似乎是您的问题所暗示的,但您也可以使用其
add
方法在迭代期间添加新元素。使用 JDK >= 8
对于那些使用 Java 8 或更高版本的人来说,您可以使用一些其他技术来利用它。
您可以在
Collection
基类中使用新的removeIf
方法:或者使用新的流 API:
在最后一种情况下,要从集合中过滤元素,您将原始引用重新分配给过滤后的集合(即
books = filtered
)或使用过滤后的集合removeAll
找到的元素原始集合(即books.removeAll(filtered)
)。使用子列表或子集
还有其他选择。如果列表已排序,并且你想删除连续的元素,你可以创建一个子列表,然后清除它:
由于子列表由原始列表支持,因此这将是删除此元素子集合的有效方法。
使用
NavigableSet.subSet
方法或此处提供的任何切片方法,可以通过排序集实现类似的效果。注意事项:
您使用什么方法可能取决于您打算做什么
removeAl
技术适用于任何集合(集合、列表、集合等)。ListIterator
技术显然只适用于列表,前提是它们给定的ListIterator
实现支持添加和删除操作。Iterator
方法适用于任何类型的集合,但它只支持删除操作。ListIterator
/Iterator
方法的明显优势是不必复制任何东西,因为我们在迭代时删除了任何东西。所以,这是非常有效的。removeAll
方法中,缺点是我们必须迭代两次。首先我们在 foor 循环中迭代寻找一个符合我们删除标准的对象,一旦找到它,我们要求将它从原始集合中删除,这意味着第二次迭代工作来寻找这个项目以便去掉它。Iterator
接口的 remove 方法在 Javadocs 中被标记为“可选”,这意味着可能有Iterator
抛出UnsupportedOperationException
实现---
如果我们调用 remove 方法。因此,如果我们不能保证迭代器支持删除元素,我会说这种方法不如其他方法安全。