让我举几个例子,用一些替代方法来避免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)或使用过滤后的集合从原始集合中删除所有找到的元素(即books.removeAll(filtered))。
使用子列表或子集
还有其他的选择。如果列表是排序的,并且你想删除连续的元素,你可以创建一个子列表,然后清除它:
books.subList(0,5).clear();
由于子列表是由原始列表支持的,这将是删除这个元素子集合的有效方法。
使用NavigableSet排序集也可以实现类似的功能。子集方法,或者这里提供的任何切片方法。
注意事项:
你使用什么方法可能取决于你打算做什么
The collect and removeAl technique works with any Collection (Collection, List, Set, etc).
The ListIterator technique obviously only works with lists, provided that their given ListIterator implementation offers support for add and remove operations.
The Iterator approach would work with any type of collection, but it only supports remove operations.
With the ListIterator/Iterator approach the obvious advantage is not having to copy anything since we remove as we iterate. So, this is very efficient.
The JDK 8 streams example don't actually removed anything, but looked for the desired elements, and then we replaced the original collection reference with the new one, and let the old one be garbage collected. So, we iterate only once over the collection and that would be efficient.
In the collect and removeAll approach the disadvantage is that we have to iterate twice. First we iterate in the foor-loop looking for an object that matches our removal criteria, and once we have found it, we ask to remove it from the original collection, which would imply a second iteration work to look for this item in order to remove it.
I think it is worth mentioning that the remove method of the Iterator interface is marked as "optional" in Javadocs, which means that there could be Iterator implementations that throw UnsupportedOperationException if we invoke the remove method. As such, I'd say this approach is less safe than others if we cannot guarantee the iterator support for removal of elements.