1. Overview
在本篇简短的教程中,我们将学习在Java中过滤集合元素的几种方法,过滤其实就是找到集合中满足条件的所有元素(某种条件下等同于删除不满足条件的元素)。
在实际的Java应用中集合过滤是一个基本的工作,为此有很多第三方类库提供了集合过滤的功能。
在本篇教程中我们使用以下方法实现集合过滤功能:
- Java 8 Streams’ filter() function
- Relevant Eclipse Collections APIs
- Apache’s CollectionUtils filter() method
- Guava’s Collections2 filter() approach
- Vavr's Stream API
2. 使用Java Stream API
自Java 8 推出以来,Streams 在集合数据处理场景发挥了巨大的作用。在大多数情况下,使用Streams都是处理集合数据的首选方法,Java 提供原生支持,不依赖任何第三方类库。
2.1. 使用 Stream.filter 对集合过滤
为了简单起见,我们所有样例代码都以提取整数集合中所有的偶数为目标。因此,集合中的所有元素都需要执行 ‘value % 2 == 0‘ 检查,在Java中可以用 Predicate 对象表示检查条件。
@Test
public void test_findEvenNumber_returnSuccess() throws Exception {
List<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> evenNumbers = findEvenNumbersUserStream(numbers);
evenNumbers.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
assertTrue(integer % 2 == 0);
}
});
}
private List<Integer> findEvenNumbersUserStream(List<Integer> baseCollection) {
Predicate<Integer> streamsPredicate = item -> item % 2 == 0;
return baseCollection.stream()
.filter(streamsPredicate)
.collect(Collectors.toList());
}
本文提供的所有可以对集合进行过滤的第三方类库都有自己特有的 *Predicate * 实现,但是所有的实现都是一个函数式接口,因此可以使用 Lambda 表达式来定义。
3. 使用 Eclipse Collections
我们利用第三方库来实现我们的目标(集合过滤),不管是我们的应用还没有使用Java 8 开发还是我们想使用第三方组件提供但是Java不具备的高级功能。
Eclipse Collections是一个努力跟上新范例,不断发展并接受所有最新Java版本引入的更改的第三方优秀Java类库。
3.1. 依赖
在 pom.xml 文件添加如下依赖即可使用 Eclipse Collections:
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections</artifactId>
<version>9.2.0</version>
</dependency>
eclipse-collections 包括所有必须的数据结构接口和API实现。
3.2. 使用 Eclipse Collections 过滤集合
基于Eclipse Collections 提供的 MutableList 数据结构实现集合过滤:
@Test
public void test_findEvenNumbersUseEclipse_returnSuccess() throws Exception {
List<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> evenNumbers = findEvenNumbersUseEclipse(numbers);
evenNumbers.forEach(integer -> assertTrue(integer % 2 == 0));
}
public List<Integer> findEvenNumbersUseEclipse(List<Integer> input) {
org.eclipse.collections.api.block.predicate.Predicate<Integer> eclipsePredicate
= item -> item % 2 == 0;
List<Integer> filteredList = Lists.mutable
.ofAll(input)
.select(eclipsePredicate);
return filteredList;
}
一种替代方案是使用 Iterate 的静态方法 select() 来实现集合过滤功能:
@Test
public void test_findEvenNumbersUseEclipseWithStaticMethod_returnSuccess() throws Exception {
List<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> evenNumbers = findEvenNumbersUseEclipseWithStaticMethod(numbers);
evenNumbers.forEach(integer -> assertTrue(integer % 2 == 0));
}
public List<Integer> findEvenNumbersUseEclipseWithStaticMethod(List<Integer> input) {
org.eclipse.collections.api.block.predicate.Predicate<Integer> eclipsePredicate
= item -> item % 2 == 0;
return (List<Integer>) Iterate.select(input, eclipsePredicate);
}
4. 使用 Apache 的 CollectionUtils
4.1. 依赖
在 pom.xml 文件添加如下依赖即可使用 Apache 的 CollectionUtils:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.2</version>
</dependency>
4.2. 使用 CollectionUtils 过滤集合
使用 CollectonUtils‘ 过滤集合的样例代码如下:
@Test
public void test_findEvenNumbersUseApache_returnSuccess() throws Exception {
List<Integer> inputs = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
Collection<Integer> numbers = new LinkedList(inputs);
Collection<Integer> evenNumbers = findEvenNumbersUseApache(numbers);
evenNumbers.forEach(integer -> assertTrue(integer % 2 == 0));
}
public Collection<Integer> findEvenNumbersUseApache(Collection<Integer> baseCollection) {
org.apache.commons.collections4.Predicate<Integer> apachePredicate = item -> item % 2 == 0;
CollectionUtils.filter(baseCollection, apachePredicate);
return baseCollection;
}
注意Apache 的 CollectionUtils 过滤方法会移除输入集合中不满足过滤条件的元素,因此要求集合是可变集合而且支持 remove 操作,否在会抛出异常。
5. 使用 Guava 的 Collections2 进行集合过滤
Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,提供了很多简化Java开发的功能,感兴趣的可以从官方文档获取更多信息。
5.1. 依赖
使用Guava之前首先引入Guava,对于maven构建的工程可以将如下依赖添加到 pom.xml 文件:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0.1-jre</version>
</dependency>
5.2. 使用 Collections2 过滤集合
Guava Collections2* 过滤集合的方式和 Apache CollectionUtils 过滤集合的方式类似,但是不需要修改原来的集合,因此不要求原集合为可变集合:
@Test
public void test_findEvenNumbersUseGuava_returnSuccess() throws Exception {
List<Integer> inputs = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
Collection<Integer> evenNumbers = findEvenNumbersUseGuava(inputs);
evenNumbers.forEach(integer -> assertTrue(integer % 2 == 0));
}
public Collection<Integer> findEvenNumbersUseGuava(Collection<Integer> baseCollection) {
com.google.common.base.Predicate<Integer> guavaPredicate = item -> item % 2 == 0;
return Collections2.filter(baseCollection, guavaPredicate);
}
6. 使用 Vavr 的 Stream 进行集合过滤
Vavr(以前称为Javaslang)是Java 8+的函数库,它提供持久数据类型和功能控制结构。
6.1. 依赖
使用Vavr之前首先引入Vavr,对于maven构建的工程可以将如下依赖添加到 pom.xml 文件
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>0.9.2</version>
</dependency>
6.2. 使用 Collections2 过滤集合
使用 Vavr的 Strem 对集合过滤和使用Java原生的 Stream 对集合过滤语法一样,而且两者都使用Java 定义的 Predicate:
@Test
public void test_findEvenNumbersUseVavr_returnSuccess() throws Exception {
List<Integer> inputs = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> evenNumbers = Stream.concat(inputs).filter(integer -> integer.intValue() % 2 == 0).asJavaMutable();
evenNumbers.forEach(integer -> assertTrue(integer % 2 == 0));
}
7. 总结
本文介绍了5种对Java 集合过滤的方法,包括Java原生支持的Stream和其他第三方库支持的方法,这些第三方库都是很好的Java开发工具,建议大家可以了解一下。
本文所有样例的源代码可以从Github获取。
8 参考
[1] How to Filter a Collection in Java
[2] eclipse-collections
[3] Apache commons-collections
[4] Guava
[5] Vavr
网友评论