如何过滤Java集合中的元素

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容