1. 概述

在本文中,我们将探讨在 Java 中过滤集合的多种方式 —— 也就是找出集合中所有满足特定条件的元素。

这是一项基础但高频的操作,几乎存在于每一个 Java 应用中。正因如此,围绕这一需求诞生了大量工具库和 API。

我们将重点介绍以下几种主流方式:

✅ Java 8 Streams 的 filter() 方法
✅ Java 9 新增的 filtering 收集器(collector)
✅ Eclipse Collections 提供的集合操作 API
✅ Apache Commons 的 CollectionUtils.filter()
✅ Guava 的 Collections2.filter()

这些方案各有特点,适用于不同场景。下面逐一展开。


2. 使用 Java Streams(Java 8+)

自从 Java 8 引入 Stream API 后,处理集合数据的方式发生了革命性变化。Stream 是目前最推荐的集合处理方式,无需引入第三方依赖,语法简洁且表达力强。

2.1 使用 Stream 进行集合过滤

为保持示例一致性,我们所有方法的目标都是:从一个 Integer 集合中筛选出所有偶数

判断条件为 value % 2 == 0,我们将它封装为一个 Predicate

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

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

⚠️ 注意:

  • filter() 接收一个 Predicate<T>,返回满足条件的元素流。
  • 最终必须通过 collect() 将流转换为具体集合(如 List)。
  • 此操作不会修改原集合,而是生成一个新集合。

虽然不同库都有自己的 Predicate 实现,但它们本质都是函数式接口,支持 Lambda 表达式写法。


2.2 Java 9 中的 filtering 收集器:分组后过滤

在 Java 8 中,如果先 filtergroupingBy,会导致某些分组因无数据而缺失。
而如果我们希望先分组,再对每个分组内部进行过滤,就需要 Java 9 新增的 filtering collector。

举个例子:我们想先按数字位数分组,然后在每组中保留偶数。

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

✅ 对比说明:

方式 行为
filter()groupingBy() 不满足条件的元素被提前丢弃,可能导致某些分组不存在
groupingBy() + filtering() 所有分组都会创建,但内部只保留符合条件的元素(可能为空列表)

选择哪种方式取决于业务需求:是否需要保留空分组结构。


3. 使用 Eclipse Collections

如果你的应用受限于 Java 版本(比如还在用 Java 7),或者追求更丰富的集合操作能力,Eclipse Collections 是一个非常强大的替代方案

它不仅提供了丰富的不可变/可变集合类型,还拥有比 JDK 更直观的函数命名(如 selectreject)。

3.1 添加依赖

pom.xml 中引入:

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

该依赖包含了核心接口和所有集合实现。


3.2 使用 Eclipse Collections 进行过滤

Eclipse Collections 中的 select() 方法相当于 filter()

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;
}

💡 小技巧:你也可以使用工具类 Iterate 的静态方法,适用于任意 Iterable

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

这种方式更加灵活,不依赖具体集合类型。


4. 使用 Apache Commons CollectionUtils

Apache Commons 是老牌工具库,commons-collections4 提供了大量实用的集合工具方法。

其中 CollectionUtils.filter() 是一个简单粗暴的过滤手段。

4.1 添加依赖

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

4.2 使用 CollectionUtils 进行过滤

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

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

⚠️ 踩坑警告:

  • 该方法会直接修改原始集合(in-place 操作)
  • 因此传入的 baseCollection 必须是可变集合(如 ArrayList
  • 若传入不可变集合(如 Collections.unmodifiableList()),会抛出 UnsupportedOperationException

✅ 优点:省内存,适合对性能敏感且允许修改原集合的场景。
❌ 缺点:破坏性操作,容易引发意外副作用,使用时务必谨慎。


5. 使用 Guava 的 Collections2

Google Guava 是另一个广受欢迎的工具库,其 Collections2 类提供了安全、非破坏性的集合操作。

5.1 添加依赖

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

5.2 使用 Collections2 进行过滤

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

✅ 关键特性:

  • 返回的是一个视图(view),不会立即创建新集合
  • 只有在遍历或调用其方法时才进行过滤判断
  • 原集合可为不可变集合,操作安全
  • 内存友好,适合大数据量场景

⚠️ 注意:由于是延迟计算,如果在过滤后修改了原集合,会影响过滤结果。这是视图机制的双刃剑。


6. 总结与选型建议

方案 是否修改原集合 是否支持延迟计算 适用场景
Java Streams ❌ 否 ✅ 是 ✅ 推荐首选,现代 Java 开发标准
Java 9 filtering collector ❌ 否 ❌ 否 分组后需进一步过滤的场景
Eclipse Collections ❌ 否 ❌ 否 需要丰富集合操作,或低版本 Java 环境
Apache CollectionUtils ✅ 是 ❌ 否 允许修改原集合,追求极致性能
Guava Collections2 ❌ 否 ✅ 是 安全视图操作,大数据量低内存消耗

📌 最终建议:

  • 新项目一律优先使用 Java Streams,简洁、安全、主流。
  • 若需兼容 Java 7 及以下,可考虑 Eclipse Collections 或 Guava。
  • Apache 的 CollectionUtils.filter() 属于“危险操作”,除非明确需要 in-place 修改,否则慎用。

所有示例代码已上传至 GitHub:https://github.com/tech-tutorial-examples/core-java-collections-filter


原始标题:How to Filter a Collection in Java