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 中,如果先 filter
再 groupingBy
,会导致某些分组因无数据而缺失。
而如果我们希望先分组,再对每个分组内部进行过滤,就需要 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 更直观的函数命名(如 select
、reject
)。
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