java - 迭代时从集合中删除元素

标签 java collections iteration

AFAIK,有两种方法:

  • 遍历集合的副本
  • 使用实际集合的迭代器

  • 例如,
    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()
    }
    

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

    最佳答案

    让我举几个例子和一些替代方法来避免 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方法的缺点是我们必须迭代两次。首先,我们在 foo 循环中迭代寻找一个符合我们移除标准的对象,一旦我们找到它,我们要求将它从原始集合中移除,这意味着第二次迭代工作以查找该项目以去掉它。
  • 我觉得值得一提的是Iterator的remove方法接口(interface)在 Javadocs 中被标记为“可选”,这意味着可能有 Iterator抛出 UnsupportedOperationException 的实现如果我们调用 remove 方法。因此,如果我们不能保证迭代器支持删除元素,我会说这种方法不如其他方法安全。
  • 关于java - 迭代时从集合中删除元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58904870/

    相关文章:

    java - ArrayList.ensureCapacity() 有性能优势吗?

    java - 如何在 Java8 中迭代 fastutil hashmap?

    java - 将 SAS 日期时间值转换为 Java YYYY-MM-DD HH :mm:ss

    java - 里面有 thymeleaf [if mso]

    java - 通过stmt.executeUpdate更新access数据库中的整数

    javascript - for 循环中的 Nightwatch 执行命令出现乱序

    r - 增加新版本 lmer 的迭代?

    java - 我们可以使用对象来访问静态成员吗​​?

    scala - 为什么 Scala 有这么多 Collection View 类型?

    python - 超出其最大大小时引发异常的集合