猿问

迭代时从集合中删除元素

迭代时从集合中删除元素

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

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


暮色呼如
浏览 819回答 3
3回答

慕运维8079593

让我举几个例子来说明一些替代方案以避免a&nbsp;ConcurrentModificationException。假设我们有以下书籍集List<Book>&nbsp;books&nbsp;=&nbsp;new&nbsp;ArrayList<Book>();books.add(new&nbsp;Book(new&nbsp;ISBN("0-201-63361-2")));books.add(new&nbsp;Book(new&nbsp;ISBN("0-201-63361-3")));books.add(new&nbsp;Book(new&nbsp;ISBN("0-201-63361-4")));收集和删除第一种技术包括收集我们想要删除的所有对象(例如使用增强的for循环),在完成迭代后,我们删除所有找到的对象。ISBN&nbsp;isbn&nbsp;=&nbsp;new&nbsp;ISBN("0-201-63361-2");List<Book>&nbsp;found&nbsp;=&nbsp;new&nbsp;ArrayList<Book>();for(Book&nbsp;book&nbsp;:&nbsp;books){ &nbsp;&nbsp;&nbsp;&nbsp;if(book.getIsbn().equals(isbn)){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;found.add(book); &nbsp;&nbsp;&nbsp;&nbsp;}}books.removeAll(found);这假设您要执行的操作是“删除”。如果你想“添加”这种方法也可以,但我想你会迭代一个不同的集合来确定你想要添加到第二个集合的元素,然后addAll在最后发布一个方法。使用ListIterator如果您正在使用列表,另一种技术包括使用a&nbsp;ListIterator,它支持在迭代过程中删除和添加项目。ListIterator<Book>&nbsp;iter&nbsp;=&nbsp;books.listIterator();while(iter.hasNext()){ &nbsp;&nbsp;&nbsp;&nbsp;if(iter.next().getIsbn().equals(isbn)){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iter.remove(); &nbsp;&nbsp;&nbsp;&nbsp;}}同样,我在上面的示例中使用了“remove”方法,这是您的问题似乎意味着什么,但您也可以使用其add方法在迭代期间添加新元素。使用JDK> = 8对于使用Java 8或高级版本的用户,可以使用其他几种技术来利用它。您可以removeIf在Collection基类中使用新方法:ISBN&nbsp;other&nbsp;=&nbsp;new&nbsp;ISBN("0-201-63361-2");books.removeIf(b&nbsp;->&nbsp;b.getIsbn().equals(other));或者使用新的流API:ISBN&nbsp;other&nbsp;=&nbsp;new&nbsp;ISBN("0-201-63361-2");List<Book>&nbsp;filtered&nbsp;=&nbsp;books.stream() &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(b&nbsp;->&nbsp;b.getIsbn().equals(other)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.collect(Collectors.toList());在最后一种情况下,要从books = filtered集合中过滤元素,可以将原始引用重新分配给过滤后的集合(即),或者将过滤后的集合用于removeAll原始集合中的已找到元素(即books.removeAll(filtered))。使用子列表或子集还有其他选择。如果列表已排序,并且您要删除连续元素,则可以创建子列表然后清除它:books.subList(0,5).clear();由于子列表由原始列表支持,因此这将是删除此元素子集的有效方法。使用NavigableSet.subSet方法的排序集或其中提供的任何切片方法可以实现类似的东西。注意事项:您使用的方法可能取决于您打算做什么收集和removeAl技术适用于任何集合(集合,列表,集等)。该ListIterator技术显然只适用于列表,前提是它们的给定ListIterator实现提供了对添加和删除操作的支持。该Iterator方法适用于任何类型的集合,但它仅支持删除操作。使用ListIterator/&nbsp;Iterator方法,显而易见的优点是不必复制任何东西,因为我们在迭代时删除。所以,这非常有效。JDK 8流示例实际上并没有删除任何内容,而是查找所需的元素,然后我们用新的集合替换原始集合引用,并让旧的集合引用被垃圾收集。因此,我们只对集合进行一次迭代,这将是有效的。在收集和removeAll接近方面,缺点是我们必须迭代两次。首先,我们在foor-loop中迭代寻找符合我们删除标准的对象,一旦找到它,我们要求将其从原始集合中删除,这意味着第二次迭代工作要查找此项目以便去掉它。我认为值得一提的是,Iterator接口的remove方法在Javadocs中标记为“可选”,这意味着如果我们调用remove方法,可能会Iterator抛出实现UnsupportedOperationException。因此,如果我们不能保证迭代器支持删除元素,我会说这种方法不如其他方法安全。

小唯快跑啊

在Java 8中,还有另一种方法。收藏#removeIf例如:List<Integer>&nbsp;list&nbsp;=&nbsp;new&nbsp;ArrayList<>();list.add(1);list.add(2);list.add(3);list.removeIf(i&nbsp;->&nbsp;i&nbsp;>&nbsp;2);

万千封印

是否有任何理由偏好一种方法而不是另一种方法第一种方法可行,但复制列表有明显的开销。第二种方法不起作用,因为许多容器在迭代期间不允许修改。这包括ArrayList。如果唯一的修改是删除当前元素,则可以通过使用itr.remove()(即使用迭代器的remove()方法而不是容器的方法)使第二种方法工作。这将是我支持的迭代器的首选方法remove()。
随时随地看视频慕课网APP

相关分类

Java
我要回答