Removing Elements from Java Collections – 从Java集合中删除元素

最后修改: 2018年 10月 24日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we’re going to talk about four different ways to remove items from Java Collections that match certain predicates.

在这个快速教程中,我们将讨论从Java Collections中删除符合某些谓词的项目的四种不同方法。

We’ll naturally also look at some of the caveats.

我们自然也会看一下一些注意事项。

2. Defining Our Collection

2.定义我们的收藏

First, we’re going to illustrate two approaches that mutate the original data structure. Then we’ll talk about two other options that instead of removing the items, will create a copy of the original Collection without them.

首先,我们要说明两种改变原始数据结构的方法。然后我们将讨论另外两种方法,它们不是删除项目,而是创建一个没有项目的原始Collection的副本。

Let’s use the following collection throughout our examples to demonstrate how we can achieve the same result using different methods:

让我们在整个例子中使用下面的集合,以证明我们如何使用不同的方法实现相同的结果。

Collection<String> names = new ArrayList<>();
names.add("John");
names.add("Ana");
names.add("Mary");
names.add("Anthony");
names.add("Mark");

3. Removing Elements With Iterator

3.用迭代器移除元素

Java’s Iterator enables us to both walk and remove every individual element within a Collection.

Java的Iterator使我们能够在Collection中行走和删除每个单独的元素。

To do so, we first need to retrieve an iterator over its elements using the iterator method. Afterward, we can visit each element with the help of next and remove them using remove:

要做到这一点,我们首先需要使用iterator方法在其元素上检索一个迭代器。之后,我们可以在next的帮助下访问每个元素,并使用remove删除它们。

Iterator<String> i = names.iterator();

while(i.hasNext()) {
    String e = i.next();
    if (e.startsWith("A")) {
        i.remove();
    }
}

Despite its simplicity, there are some caveats we should consider:

尽管它很简单,但有一些我们应该考虑的注意事项。

  • Depending on the collection we may run into ConcurrentModificationException exceptions
  • We need to iterate over the elements before we can remove them
  • Depending on the collection, remove may behave differently than expected. E.g.: ArrayList.Iterator removes the element from the collection and shifts subsequent data to the left whereas, LinkedList.Iterator simply adjusts the pointer to the next element. As such, LinkedList.Iterator performs much better than ArrayList.Iterator when removing items

4. Java 8 and Collection.removeIf()

4.Java 8和Collection.removeIf()

Java 8 introduced a new method to the Collection interface that provides a more concise way to remove elements using Predicate:

Java 8为Collection接口引入了一个新的方法,提供了一种更简洁的方式,使用Predicate

names.removeIf(e -> e.startsWith("A"));

It’s important to note that contrary to the Iterator approach, removeIf performs similarly well in both LinkedList and ArrayList.

值得注意的是,与Iterator方法相反,removeIfLinkedListArrayList中的表现都差不多。

In Java 8, ArrayList overrides the default implementation – which relies on Iterator – and implements a different strategy: first, it iterates over the elements and marks the ones that match our Predicate; afterward, it iterates a second time to remove (and shift) the elements that were marked in the first iteration.

在Java 8中,ArrayList覆盖了默认的实现–它依赖于Iterator–并实现了一个不同的策略:首先,它遍历元素并标记符合我们Predicate的元素;之后,它第二次遍历以移除(并转移)第一次遍历中标记的元素。

5. Java 8 and the Introduction of Stream

5.Java 8和Stream的引入

One of the new major features in Java 8 was the addition of Stream (and Collectors). There are many ways to create a Stream from a source. However, most operations that affect the Stream instance won’t mutate its source, rather, the API focuses on creating copies of a source and performing any operation we may need in them.

Java 8的新的主要功能之一是增加了Stream(和Collectors)。有很多方法可以从一个源创建一个Stream。然而,大多数影响Stream实例的操作不会改变它的源,相反,API专注于创建源的副本并在其中执行我们可能需要的任何操作。

Let’s take a look at how we can use Stream and Collectors to find/filter elements that match, and don’t match, our Predicate.

让我们来看看我们如何使用StreamCollectors来查找/过滤与我们的Predicate相匹配和不相匹配的元素。

5.1. Removing Elements With Stream

5.1.用Stream删除元素

Removing, or rather, filtering elements using Stream is quite straightforward, we just need to create an instance of Stream using our Collection, invoke filter with our Predicate and then collect the result with the help of Collectors:

删除,或者说,使用Stream过滤元素是非常直接的,我们只需要使用我们的Collection创建一个Stream的实例,调用filter与我们的Predicate,然后collect借助Collectors:的结果。

Collection<String> filteredCollection = names
  .stream()
  .filter(e -> !e.startsWith("A"))
  .collect(Collectors.toList());

Streaming is less invasive than the previous approaches, it promotes isolation and enables the creation of multiple copies from the same source. However, we should keep in mind that it also increases the memory used by our application.

Streaming比之前的方法更具侵略性,它促进了隔离,并能从同一来源创建多个副本。然而,我们应该记住,它也增加了我们应用程序的内存使用。

5.2. Collectors.partitioningBy

5.2.Collectors.partitioningBy

Combining both Stream.filter and Collectors is quite handy, although we may run into scenarios where we need both matching and non-matching elements. In such cases we can take advantage of Collectors.partitioningBy:

结合Stream.filterCollectors是相当方便的,尽管我们可能会遇到同时需要匹配和非匹配元素的情况。在这种情况下,我们可以利用Collectors.partitioningBy的优势。

Map<Boolean, List<String>> classifiedElements = names
    .stream()
    .collect(Collectors.partitioningBy((String e) -> 
      !e.startsWith("A")));

String matching = String.join(",",
  classifiedElements.get(true));
String nonMatching = String.join(",",
  classifiedElements.get(false));

This method returns a Map that only contains two keys, true and false, each pointing to a list that contains the matching, and non-matching elements, respectively.

这个方法返回一个Map,它只包含两个键,truefalse,每个键都指向一个列表,分别包含匹配和非匹配的元素。

6. Conclusion

6.结论

In this article, we looked at some methods to remove elements from Collections and some of their caveats.

在这篇文章中,我们看了一些从Collections中移除元素的方法以及它们的一些注意事项。

You can find the complete source code and all code snippets for this article over on GitHub.

你可以在GitHub上找到本文的完整源代码和所有代码片段