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

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

问题描述

AFAIK有两种方法:


  1. 在集合的副本上迭代

  2. 使用实际收集的迭代器

例如,

  List< Foo> fooListCopy = new ArrayList< Foo>(fooList); 
for(Foo foo:fooListCopy){
//修改实际fooList
}

  Iterator< Foo> itr = fooList.iterator(); 
while(itr.hasNext()){
//使用itr.remove修改实际fooList
}

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

解决方案

避免 ConcurrentModificationException 的替代方案是:

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

假设您要执行的操作是删除。



如果你想添加这个方法也可以工作,但我假设你将迭代不同的集合,以确定要添加到第二个元素



或者您可以使用 ListIterator ,它在迭代过程中支持一个remove / add方法。

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

同样,我使用remove你的问题似乎暗示,但你也可以使用它的 add 方法在迭代过程中添加新的元素。



或者,您可以使用 LambdaJ 等第三方库,并在后台为您提供所有工作>

 列表< Book> filtered = select(books,
have(on(Book.class).getIsbn(),
is(new ISBN(0-201-63361-2))));

或使用JDK 8流,lambdas / closures:

  ISBN其他=新ISBN(0-201-63361-2); 
List< Book> filtered = books.stream()
.filter(b - > b.getIsbn()。equals(other))
.collect(Collectors.toList

在这两种情况下,过滤集合中的元素,并将原始引用重新分配给过滤的集合(即 books = filtered )或使用过滤后的集合 removeAll 注意事项:

b

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




  • 第一种方法适用于任何集合(Collection,List,Set等)。

  • 第二个仅适用于列表,前提是它们的给定ListIterator支持添加和删除操作。

  • 如果您只打算使用迭代器的remove方法,第二种方法适用于任何集合。

  • 在第二种方法中,不得不复制任何东西。

  • 第三个实际上没有删除任何内容,但是查找所需的元素,然后可以替换新的引用,并让旧的引用被垃圾回收。

  • 在第一种方法中,缺点是我们必须重复两次。我们在foor-loop中迭代寻找一个元素,一旦我们找到它,我们要求从原始列表中删除它,这将意味着第二次迭代工作寻找这个给定的项目。

  • 我认为值得一提的是在Javadocs中将 Iterator 接口的remove方法标记为可选,这意味着是可能引发 UnsupportedOperationException 的I​​terator实现。
  • / p>

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

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

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



    使用 NavigableSet.subSet 方法或任何提供的切片方法


    AFAIK, there are two approaches:

    1. Iterate over a copy of the collection
    2. Use the iterator of the actual collection

    For instance,

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

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

    解决方案

    Your alternatives to avoid a ConcurrentModificationException are:

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

    Collect all the records that you want to delete within an enhanced for loop, and after you finish iterating, you remove all found records.

    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, supposing that the operation you want to do is "delete".

    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.

    Or you may use a ListIterator which has support for a remove/add method during the iteration itself.

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

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

    Or you may use a third-party library like LambdaJ and it makes all the work for you behind the scenes>

    List<Book> filtered = select(books, 
                    having(on(Book.class).getIsbn(), 
                            is(new ISBN("0-201-63361-2"))));
    

    Or using JDK 8 streams, lambdas/closures:

    ISBN other = new ISBN("0-201-63361-2");
    List<Book> filtered = books.stream()
                               .filter(b -> b.getIsbn().equals(other))
                               .collect(Collectors.toList());
    

    In these two last cases to filter elements out of a collection and 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)).

    Considerations:

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

    • The first method works with any Collection (Collection, List, Set, etc).
    • The second works only with Lists, provided that their given ListIterator offers support for add and remove operations.
    • The second approach would work on any collection if you only intend to use the iterator's remove method.
    • In the second approach the obvious advantage is not having to copy anything.
    • The third does not actually remove anything, but looks for the desired elements, then you can replace the original reference for the new one, and let the old one be garbage collected.
    • In the first approach the disadvantage is that we have to iterate twice. We iterate in the foor-loop looking for an element, and once we find it, we ask to remove it from the original list, which would imply a second iteration work to look for this given item.
    • I think it is worth mentioning the the remove method of the Iterator interface is marked as optional in Javadocs, which means that there could be Iterator implementations that may throw UnsupportedOperationException. As such, I'd say this approach is less safe than the first one.

    Other Alternatives

    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.

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

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

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