How to Filter a Collection in Java – 如何在Java中过滤一个集合

最后修改: 2018年 7月 30日

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

1. Overview

1.概述

In this short tutorial, we’ll have a look at different ways of filtering a Collection in Java – that is, finding all the items that meet a certain condition.

在这个简短的教程中,我们将看看在Java中过滤一个集合的不同方法–也就是说,找到所有符合某个条件的项目。

This is a fundamental task that is present in practically any Java application.

这是一项基本任务,实际上存在于任何Java应用程序中。

For this reason, the number of libraries that provide functionality for this purpose is significant.

由于这个原因,为这一目的提供功能的库的数量是很大的。

Particularly, in this tutorial we’ll cover:

特别是,在本教程中,我们将涵盖。

  • Java 8 Streams’ filter() function
  • Java 9 filtering collector
  • Relevant Eclipse Collections APIs
  • Apache’s CollectionUtils filter() method
  • Guava’s Collections2 filter() approach

2. Using Streams

2.使用流

Since Java 8 was introduced, Streams have gained a key role in most cases where we have to process a collection of data.

自Java 8推出以来,Streams在我们必须处理数据集合的大多数情况下获得了关键作用。

Consequently, this is the preferred approach in most cases as it is built in Java and requires no additional dependencies.

因此,在大多数情况下,这是首选的方法,因为它是在Java中构建的,不需要额外的依赖性。

2.1. Filtering a Collection with Streams

2.1.用Streams过滤一个集合

For the sake of simplicity, in all the examples our objective will be to create a method that retrieves only the even numbers from a Collection of Integer values.

为了简单起见,在所有的例子中,我们的目标是创建一个方法,从CollectionInteger值中只检索出偶数。

Thus, we can express the condition that we’ll use to evaluate each item as ‘value % 2 == 0‘.

因此,我们可以将我们用来评估每个项目的条件表达为’value % 2 == 0‘。

In all the cases, we’ll have to define this condition as a Predicate object:

在所有的情况下,我们必须把这个条件定义为一个Predicate对象。

public Collection<Integer> findEvenNumbers(Collection<Integer> baseCollection) {
    Predicate<Integer> streamsPredicate = item -> item % 2 == 0;

    return baseCollection.stream()
      .filter(streamsPredicate)
      .collect(Collectors.toList());
}

It’s important to note that each library we analyze in this tutorial provides its own Predicate implementation, but that still, all of them are defined as functional interfaces, therefore allowing us to use Lambda functions to declare them.

需要注意的是,我们在本教程中分析的每个库都提供了自己的Predicate 实现,但仍然是所有的库都被定义为功能接口,因此允许我们使用Lambda函数来声明它们。

In this case, we used a predefined Collector provided by Java that accumulates the elements into a List, but we could’ve used others, as discussed in this previous post.

在这个案例中,我们使用了Java提供的一个预定义的Collector,它将元素累积到一个List中,但我们也可以使用其他的,正如在这篇上篇文章中讨论的那样。

2.2. Filtering After Grouping a Collection in Java 9

2.2.在Java 9中对一个集合进行分组后进行过滤

Streams allow us to aggregate items using the groupingBy collector.

流允许我们使用groupingBy collector来聚合项目。

Yet, if we filter as we did in the last section, some elements might get discarded in an early stage, before this collector comes into play.

然而,如果我们像上一节那样进行过滤,一些元素可能会在早期阶段被丢弃,在这个收集器发挥作用之前。

For this reason, the filtering collector was introduced with Java 9, with the objective of processing the subcollections after they have been grouped.

出于这个原因,过滤收集器被引入Java 9,目的是在子收集被分组后进行处理。

Following our example, let’s imagine we want to group our collection based on the number of digits each Integer has, before filtering out the odd numbers:

按照我们的例子,让我们设想一下,在过滤掉奇数之前,我们要根据每个整数的位数来分组。

public Map<Integer, List<Integer>> findEvenNumbersAfterGrouping(
  Collection<Integer> baseCollection) {
 
    Function<Integer, Integer> getQuantityOfDigits = item -> (int) Math.log10(item) + 1;
    
    return baseCollection.stream()
      .collect(groupingBy(
        getQuantityOfDigits,
        filtering(item -> item % 2 == 0, toList())));
}

In short, if we use this collector, we might end up with an empty value entry, whereas if we filter before grouping, the collector wouldn’t create such an entry at all.

简而言之,如果我们使用这个收集器,我们最终可能会得到一个空值条目,而如果我们在分组之前进行过滤,收集器根本不会创建这样一个条目。

Of course, we would choose the approach based on our requirements.

当然,我们会根据自己的要求来选择方法。

3. Using Eclipse Collections

3.使用Eclipse集合

We can also make use of some other third-party libraries to accomplish our objective, either if its because our application doesn’t support Java 8 or because we want to take advantage of some powerful functionality not provided by Java.

我们还可以利用其他一些第三方库来实现我们的目标,这可能是因为我们的应用程序不支持Java 8,或者是因为我们想利用一些Java没有提供的强大功能。

Such is the case of Eclipse Collections, a library that strives to keep up with the new paradigms, evolving and embracing the changes introduced by all the latest Java releases.

这就是Eclipse Collections的情况,这个库努力跟上新的范式,不断发展并拥抱所有最新的Java版本所带来的变化。

We can begin by exploring our Eclipse Collections Introductory post to have a broader knowledge of the functionality provided by this library.

我们可以从探索我们的Eclipse Collections介绍帖子开始,对这个库所提供的功能有一个更广泛的了解。

3.1. Dependencies

3.1. 依赖性

Let’s begin by adding the following dependency to our project’s pom.xml:

让我们首先在我们项目的pom.xml中添加以下依赖关系。

<dependency>
    <groupId>org.eclipse.collections</groupId>
    <artifactId>eclipse-collections</artifactId>
    <version>9.2.0</version>
</dependency>

The eclipse-collections includes all the necessary data structure interfaces and the API itself.

eclipse-collections包括所有必要的数据结构接口和API本身。

3.2. Filtering a Collection with Eclipse Collections

3.2.用Eclipse Collections过滤一个集合

Let’s now use eclipse’s filtering functionality on one of its data structures, such as its MutableList:

现在让我们在它的一个数据结构上使用eclipse的过滤功能,比如说它的MutableList

public Collection<Integer> findEvenNumbers(Collection<Integer> baseCollection) {
    Predicate<Integer> eclipsePredicate
      = item -> item % 2 == 0;
 
    Collection<Integer> filteredList = Lists.mutable
      .ofAll(baseCollection)
      .select(eclipsePredicate);

    return filteredList;
}

As an alternative, we could’ve used the Iterate‘s select() static method to define the filteredList object:

作为替代,我们可以使用Iterateselect()静态方法来定义filteredList对象。

Collection<Integer> filteredList
 = Iterate.select(baseCollection, eclipsePredicate);

4. Using Apache’s CollectionUtils

4.使用Apache的CollectionUtils

To get started with Apache’s CollectionUtils library, we can check out this short tutorial where we covered its uses.

要开始使用Apache的CollectionUtils库,我们可以查看这个简短的教程,其中我们介绍了它的用途。

In this tutorial, however, we’ll focus on its filter() implementation.

然而,在本教程中,我们将专注于它的 filter()实现。

4.1. Dependencies

4.1. 依赖性

First, we’ll need the following dependencies in our pom.xml file:

首先,我们需要在我们的pom.xml文件中加入以下依赖项。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.2</version>
</dependency>

4.2. Filtering a Collection with CollectionUtils

4.2.用CollectionUtils过滤一个集合

We are now ready to use the CollectonUtils‘ methods:

我们现在准备使用CollectonUtils的方法。

public Collection<Integer> findEvenNumbers(Collection<Integer> baseCollection) {
    Predicate<Integer> apachePredicate = item -> item % 2 == 0;

    CollectionUtils.filter(baseCollection, apachePredicate);
    return baseCollection;
}

We have to take into account that this method modifies the baseCollection by removing every item that doesn’t match the condition.

我们必须考虑到这个方法会修改baseCollection,删除每个不符合条件的项目。

This means that the base Collection has to be mutable, otherwise it will throw an exception.

这意味着基础集合必须是可变的,否则将抛出一个异常

5. Using Guava’s Collections2

5.使用Guava的Collections2

As before, we can read our previous post ‘Filtering and Transforming Collections in Guava’ for further information on this subject.

和以前一样,我们可以阅读我们以前的文章‘Filtering and Transforming Collections in Guava’,以了解关于这个问题的进一步信息。

5.1. Dependencies

依赖性

Let’s start by adding this dependency in our pom.xml file:

让我们首先在我们的pom.xml文件中添加这个依赖性

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

5.2. Filtering a Collection with Collections2

5.2.用Collections2过滤一个集合

As we can see, this approach is fairly similar to the one followed in the last section:

我们可以看到,这种方法与上一节中的方法相当相似。

public Collection<Integer> findEvenNumbers(Collection<Integer> baseCollection) {
    Predicate<Integer> guavaPredicate = item -> item % 2 == 0;
        
    return Collections2.filter(baseCollection, guavaPredicate);
}

Again, here we define a Guava specific Predicate object.

同样,在这里我们定义了一个Guava特定的Predicate对象。

In this case, Guava doesn’t modify the baseCollection, it generates a new one, so we can use an immutable collection as input.

在这种情况下,Guava并没有修改baseCollection,而是生成了一个新的,所以我们可以使用一个不可变的集合作为输入。

6. Conclusion

6.结语

In summary, we’ve seen that there are many different ways of filtering collections in Java.

综上所述,我们已经看到,在Java中有许多不同的过滤集合的方法。

Even though Streams are usually the preferred approach, its good to know and keep in mind the functionality offered by other libraries.

尽管Streams通常是首选的方法,但了解并牢记其他库提供的功能是很好的。

Especially if we need to support older Java versions. However, if this is the case, we need to keep in mind recent Java features used throughout the tutorial such as lambdas should be replaced with anonymous classes.

特别是如果我们需要支持旧的Java版本。然而,如果是这样的话,我们需要牢记整个教程中使用的最近的Java特性,如lambdas应该用匿名类来代替。

As usual, we can find all the examples shown in this tutorial in our Github repo.

像往常一样,我们可以在我们的Github repo中找到本教程中显示的所有示例。