让我举几个例子来避免ConcurrentModificationException
异常。
假设我们有以下书籍集合:
List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));
收集并删除
第一种技术是收集所有需要删除的对象(例如使用增强型for循环),在迭代结束后,删除所有找到的对象。
ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);
假设你想进行的操作是“删除”。
如果你想要“添加”,这种方法也可以,但我会假设你将迭代另一个集合来确定你想要添加到第二个集合中的元素,然后在最后发出 addAll
方法。
使用ListIterator
如果你正在使用列表,那么另一种技术是使用 ListIterator
,它支持在迭代过程中删除和添加项目。
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
在上面的示例中,我再次使用了"remove"方法,这是您的问题似乎暗示的,但是您还可以在迭代过程中使用它的add
方法来添加新元素。
使用JDK >= 8
对于那些使用Java 8或更高版本的人来说,有几种其他技术可以利用它。
您可以使用Collection
基类中的新removeIf
方法:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
或使用新的流 API:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
在最后一种情况下,为了从集合中过滤元素,您可以重新分配原始引用到已过滤的集合(即books = filtered
),或者使用已过滤的集合来removeAll
在原始集合中找到的元素(即books.removeAll(filtered)
)。
使用子列表或子集
还有其他选择。如果列表已排序,并且您想要删除连续的元素,则可以创建一个子列表,然后清除它:
books.subList(0,5).clear();
由于子列表是基于原始列表支持的,因此这将是一种有效的方法来删除这个子集合中的元素。
使用NavigableSet.subSet
方法或其中提供的任何切片方法可以实现类似的效果,也适用于排序集。
考虑因素:
你使用的方法可能取决于你打算做什么。
- collect和
removeAll
技术适用于任何集合类型(Collection、List、Set等)。
ListIterator
技术显然只适用于列表,前提是给定的ListIterator
实现支持添加和删除操作。
Iterator
方法适用于任何类型的集合,但只支持删除操作。
- 使用
ListIterator
/Iterator
方法的明显优势在于不需要复制任何内容,因为我们边迭代边移除。因此,这非常高效。
- JDK 8流程示例实际上没有删除任何内容,而是查找所需的元素,然后用新的集合引用替换原始集合引用,并将旧的集合回收。因此,我们仅一次遍历集合即可,这非常高效。
- collect和
removeAll
方法的缺点在于需要进行两次迭代。首先,我们在for循环中查找与我们的删除条件匹配的对象,一旦找到,我们请求将其从原始集合中删除,这将意味着第二次迭代工作查找该项以便将其删除。
- 值得一提的是,
Iterator
接口的remove方法在Javadocs中标记为“可选”。这意味着,如果我们调用remove方法,可能会出现抛出UnsupportedOperationException
的Iterator
实现。因此,如果我们不能保证迭代器支持元素的删除,我认为这种方法比其他方法更不安全。
while
和for
有不同的作用域规则。 - Alexander Mills