迭代时从集合中删除元素 [英] Remove elements from collection while iterating

查看:57
本文介绍了迭代时从集合中删除元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

AFAIK,有两种方法:

AFAIK, there are two approaches:

  1. 迭代集合的副本
  2. 使用实际集合的迭代器

例如

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()
}

是否有任何理由更喜欢一种方法而不是另一种方法(例如,出于可读性的简单原因,更喜欢第一种方法)?

Are there any reasons to prefer one approach over the other (e.g. preferring the first approach for the simple reason of readability)?

推荐答案

让我举几个例子和一些替代方案来避免 ConcurrentModificationException.

Let me give a few examples with some alternatives to avoid a 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 循环),并在我们完成迭代后,删除所有找到的对象.

The first technique consists in collecting all the objects that we want to delete (e.g. using an enhanced for loop) and after we finish iterating, we remove all found objects.

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);

这里假设您要执行的操作是删除".

This is supposing that the operation you want to do is "delete".

如果你想添加"这个方法也可以,但我假设你会迭代一个不同的集合来确定你想要添加到第二个集合的元素,然后发出一个 addAll 方法在最后.

If you want to "add" this approach would also work, but I would assume you would iterate over a different collection to determine what elements you want to add to a second collection and then issue an addAll method at the end.

使用 ListIterator

如果您正在使用列表,另一种技术是使用 ListIterator,它支持在迭代过程中删除和添加项目.

If you are working with lists, another technique consists in using a ListIterator which has support for removal and addition of items during the iteration itself.

ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
    if(iter.next().getIsbn().equals(isbn)){
        iter.remove();
    }
}

同样,我在上面的示例中使用了remove"方法,这就是您的问题似乎暗示的内容,但您也可以使用其 add 方法在迭代期间添加新元素.

Again, I used the "remove" method in the example above which is what your question seemed to imply, but you may also use its add method to add new elements during iteration.

使用 JDK >= 8

对于使用 Java 8 或更高版本的人,您可以使用其他几种技术来利用它.

For those working with Java 8 or superior versions, there are a couple of other techniques you could use to take advantage of it.

您可以在 Collection 基类中使用新的 removeIf 方法:

You could use the new removeIf method in the Collection base class:

ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));

或者使用新的流 API:

Or use the new stream 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)).

In this last case, to filter elements out of a collection, you reassign the original reference to the filtered collection (i.e. books = filtered) or used the filtered collection to removeAll the found elements from the original collection (i.e. books.removeAll(filtered)).

使用子列表或子集

还有其他选择.如果列表已排序,并且您想删除连续的元素,您可以创建一个子列表然后将其清除:

There are other alternatives as well. If the list is sorted, and you want to remove consecutive elements you can create a sublist and then clear it:

books.subList(0,5).clear();

由于子列表由原始列表支持,这将是删除此元素子集合的有效方法.

Since the sublist is backed by the original list this would be an efficient way of removing this subcollection of elements.

使用 NavigableSet.subSet 方法或那里提供的任何切片方法可以通过排序集实现类似的效果.

Something similar could be achieved with sorted sets using NavigableSet.subSet method, or any of the slicing methods offered there.

注意事项:

您使用的方法可能取决于您打算做什么

What method you use might depend on what you are intending to do

  • collect 和 removeAl 技术适用于任何集合(集合、列表、集合等).
  • ListIterator 技术显然只适用于列表,前提是它们给定的 ListIterator 实现提供对添加和删除操作的支持.
  • Iterator 方法适用于任何类型的集合,但它只支持删除操作.
  • 使用 ListIterator/Iterator 方法的明显优势是不必复制任何内容,因为我们在迭代时删除.所以,这是非常有效的.
  • JDK 8 流示例实际上并未删除任何内容,而是查找所需元素,然后我们将原始集合引用替换为新集合,并让旧集合进行垃圾回收.因此,我们只对集合迭代一次,这样会很高效.
  • 在 collect 和 removeAll 方法中,缺点是我们必须迭代两次.首先我们在for循环中迭代寻找符合我们移除条件的对象,一旦我们找到它,我们要求将它从原始集合中移除,这意味着第二次迭代工作来寻找这个项目以便去掉它.
  • 我觉得值得一提的是,Iterator接口的remove方法在Javadocs中被标记为可选",这意味着可能有Iterator实现抛出UnsupportedOperationException 如果我们调用 remove 方法.因此,如果我们不能保证迭代器支持删除元素,我会说这种方法不如其他方法安全.
  • 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.

这篇关于迭代时从集合中删除元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆