当前位置: 首页 > 知识库问答 >
问题:

在迭代时从集合中删除元素

柴飞星
2023-03-14

AFAIK,有两种方法:

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

有没有理由偏爱一种方法而不是另一种方法(例如,由于可读性的简单原因而偏爱第一种方法)?

共有2个答案

高奇
2023-03-14

在《Java 8》中,还有另一种做法。集合#RemoveIf

例如:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

list.removeIf(i -> i > 2);
从智明
2023-03-14

让我举几个例子,其中有一些替代方法来避免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);

这是假设您要执行的操作是“delete”。

如果您想要“添加”,这种方法也会起作用,但我假设您将迭代一个不同的集合,以确定要将哪些元素添加到第二个集合中,然后在结尾发出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(filtereded))。

使用子列表或子集

还有其他的选择。如果列表已排序,并且希望删除连续元素,则可以创建子列表,然后将其清除:

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

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

使用navigableSet.subset方法或其中提供的任何切片方法,可以对排序集实现类似的功能。

注意事项:

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

  • 收集和removeal技术适用于任何集合(收集、列表、集等)。
  • ListIterator技术显然只适用于列表,前提是其给定的ListIterator实现提供对添加和删除操作的支持。
  • 迭代器方法可用于任何类型的集合,但它只支持删除操作。
  • 使用ListIterator/Iterator方法,明显的优点是不必复制任何内容,因为我们在迭代时删除。所以,这是非常有效的。
  • JDK8Streams示例实际上并没有删除任何内容,而是查找所需的元素,然后我们用新的元素替换原来的收集引用,并让旧的元素被垃圾收集。因此,我们只在集合上迭代一次,这将是高效的。
  • 在collect和RemoveAll方法中,缺点是我们必须迭代两次。首先,我们在foor循环中进行迭代,寻找一个符合移除标准的对象,一旦找到它,我们就要求将它从原始集合中移除,这意味着需要进行第二次迭代工作来寻找这个项,以便将其移除。
  • 我认为值得一提的是,iterator接口的remove方法在Javadocs中被标记为“可选”,这意味着如果我们调用remove方法,可能会有iterator实现抛出unsupportedoperationexception。因此,如果我们不能保证迭代器对元素移除的支持,我会说这种方法比其他方法更不安全。
 类似资料:
  • 问题内容: AFAIK有两种方法: 遍历集合的副本 使用实际集合的迭代器 例如, 和 是否有任何理由偏爱一种方法(例如,出于可读性的简单原因而偏爱第一种方法)? 问题答案: 让我举几个例子,并提出一些避免方案。 假设我们有以下藏书 收集并删除 第一种技术是收集所有要删除的对象(例如,使用增强的for循环),并在完成迭代后删除所有找到的对象。 假设你要执行的操作是“删除”。 如果要“添加”此方法也可

  • 问题内容: 我正在对JRE 进行迭代,该JRE 强制执行快速失败迭代器概念,因此如果在迭代时修改了,则会抛出a ,而不是使用method。但是,如果对象满足条件,则需要删除该对象的“逻辑伙伴”。从而阻止伙伴也被处理。我怎样才能做到这一点?也许为此使用更好的收集类型? 例。 谢谢。 问题答案: 您要从列表中删除项目,然后继续在同一列表中进行迭代。您是否可以实施两步解决方案,即在步骤1中将要删除的项目

  • 问题内容: 最近,我编写此代码时并没有考虑太多: 其中,将删除该项目。 现在看一下代码,我对为什么它甚至起作用感到困惑-我是否不应该得到类似的例外?当使用常规的for-in循环时,甚至可以使用相同的代码! 这是预期的行为吗?还是我很幸运,它没有崩溃? 问题答案: 这确实是预期的行为–这是由于Swift(以及标准库中的许多其他集合)是一种具有写时复制语义的值类型的事实。这意味着其基础缓冲区(间接存储

  • 问题内容: 我希望能够在迭代过程中从集合中删除多个元素。最初,我希望迭代器足够聪明,以使下面的幼稚解决方案能够正常工作。 但这会引发一个错误。 请注意,就我所知,iterator.remove()无法正常工作,因为我需要一次删除多个内容。还假设不可能确定“即时”删除哪些元素,但是可以编写该方法。在我的特定情况下,要确定要在迭代过程中删除的内容,将占用大量内存和处理时间。由于内存限制,也无法制作副本

  • 问题内容: 假设我有一个数字列表: 迭代时,如何从列表中删除元素(例如3)? 我尝试了以下代码,但没有成功: 有任何想法吗? 谢谢,博达·西多(Boda Cydo)。 问题答案: 最好的方法通常是建设性地进行-建立所需项目的新列表,而不要删除不需要的项目。例如: 列表理解建立所需的列表,并分配到“全名单切片” ,确保你 不 只是一个重新绑定的名字,但完全替换 内容 ,所以影响是恒等于你想要的“清除

  • 我已经在这上面耽搁了一段时间了。我正在尝试删除一个集合的元素,如果他们制定了一个集合的标准。然而,在迭代时,当我试图移除元素时,它失败了。 我得到java.util.concurrentModificationException 如有任何建议,将不胜感激。