让我举几个例子,并提出一些避免方案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();
}
}
同样,在上面的示例中,我使用了“删除”方法,这似乎是您的问题所暗示的含义,但是您也可以add
在迭代过程中使用其方法添加新元素。
使用JDK> = 8
对于使用Java 8或更高版本的用户,您可以使用多种其他技术来利用它。
您可以removeIf
在Collection
基类中使用新方法:
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
方法或那里提供的任何切片方法,可以通过排序集实现类似的效果。
注意事项:
您使用哪种方法可能取决于您打算做什么
- 收集和
removeAl
集合技术可与任何集合(集合,列表,集合等)一起使用。
ListIterator
显然,该技术仅适用于列表,前提是它们的给定ListIterator
实现为添加和删除操作提供了支持。
- 该
Iterator
方法适用于任何类型的集合,但仅支持删除操作。
- 使用
ListIterator
/ Iterator
方法,明显的优点是不必复制任何内容,因为我们在迭代时将其删除。因此,这非常有效。
- JDK 8流示例实际上并没有删除任何内容,而是在查找所需的元素,然后我们用新的引用替换了原始的收集引用,并让旧的引用被垃圾回收了。因此,我们只对集合进行一次迭代,这样会很有效。
- 在收集和
removeAll
处理中,缺点是我们必须迭代两次。首先,我们在foor循环中迭代,以寻找一个符合移除条件的对象,找到后,我们要求将其从原始集合中移除,这意味着需要进行第二次迭代来寻找该项目,以便去掉它。
- 我认为值得一提的是,该
Iterator
接口的remove方法在Javadocs中被标记为“可选”,这意味着如果我们调用remove方法,可能会Iterator
抛出一些实现UnsupportedOperationException
。因此,如果我们不能保证迭代器支持删除元素,那么这种方法将不如其他方法安全。