迭代时从集合中删除元素

2024-03-14

AFAIK,有两种方法:

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

例如,

List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
    // modify actual fooList
}

and

Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
    // modify actual fooList using itr.remove()
}

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


让我举几个例子,并提供一些替代方案,以避免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<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该技术适用于任何集合(集合、列表、集合等)。
  • The ListIterator技术显然只适用于列表,前提是它们给定的ListIterator实现提供了对添加和删除操作的支持。
  • The Iterator方法适用于任何类型的集合,但它仅支持删除操作。
  • 随着ListIterator/Iterator方法的明显优点是不必复制任何内容,因为我们在迭代时删除了任何内容。所以,这是非常有效的。
  • JDK 8 流示例实际上并没有删除任何内容,而是查找所需的元素,然后我们用新的集合引用替换原始的集合引用,并让旧的集合引用被垃圾回收。因此,我们只对集合进行一次迭代,这样会很高效。
  • 在收集和removeAll该方法的缺点是我们必须迭代两次。首先,我们在 foo 循环中迭代,寻找与我们的删除标准相匹配的对象,一旦找到它,我们就要求将其从原始集合中删除,这意味着第二次迭代工作来查找该项目,以便去掉它。
  • 我认为值得一提的是remove方法Iterator接口在 Javadocs 中被标记为“可选”,这意味着可能有Iterator抛出的实现UnsupportedOperationException如果我们调用remove方法。因此,如果我们不能保证迭代器支持删除元素,我会说这种方法不如其他方法安全。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

迭代时从集合中删除元素 的相关文章

随机推荐