乐趣区

Java-Stream函数式编程第三篇管道流结果处理

一、Java Stream 管道数据处理操作

在本号之前写过的文章中,曾经给大家介绍过 Java Stream 管道流是用于简化集合类元素处理的 java API。在使用的过程中分为三个阶段。在开始本文之前,我觉得仍然需要给一些新朋友介绍一下这三个阶段,如图:

  • 第一阶段(图中蓝色):将集合、数组、或行文本文件转换为 java Stream 管道流
  • 第二阶段(图中虚线部分):管道流式数据处理操作,处理管道中的每一个元素。上一个管道中的输出元素作为下一个管道的输入元素。
  • 第三阶段(图中绿色):管道流结果处理操作,也就是本文的将介绍的核心内容。

在开始学习之前,仍然有必要回顾一下我们之前给大家讲过的一个例子:


List<String> nameStrs = Arrays.asList("Monkey", "Lion", "Giraffe","Lemur");

List<String> list = nameStrs.stream()
        .filter(s -> s.startsWith("L"))
        .map(String::toUpperCase)
        .sorted()
        .collect(toList());
System.out.println(list);
  • 首先使用 stream()方法将字符串 List 转换为管道流 Stream
  • 然后进行管道数据处理操作,先用 fliter 函数过滤所有大写 L 开头的字符串,然后将管道中的字符串转换为大写字母 toUpperCase,然后调用 sorted 方法排序。这些 API 的用法在本号之前的文章有介绍过。其中还使用到了 lambda 表达式和函数引用。
  • 最后使用 collect 函数进行结果处理,将 java Stream 管道流转换为 List。最终 list 的输出结果是:[LEMUR, LION]

如果你不使用 java Stream 管道流的话,想一想你需要多少行代码完成上面的功能呢?回到正题,这篇文章就是要给大家介绍第三阶段:对管道流处理结果都可以做哪些操作呢?下面开始吧!

二、ForEach 和 ForEachOrdered

如果我们只是希望将 Stream 管道流的处理结果打印出来,而不是进行类型转换,我们就可以使用 forEach()方法或 forEachOrdered()方法。


Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
        .parallel()
        .forEach(System.out::println);
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
        .parallel()
        .forEachOrdered(System.out::println);
  • parallel()函数表示对管道中的元素进行并行处理,而不是串行处理,这样处理速度更快。但是这样就有可能导致管道流中后面的元素先处理,前面的元素后处理,也就是元素的顺序无法保证
  • forEachOrdered 从名字上看就可以理解,虽然在数据处理顺序上可能无法保障,但是 forEachOrdered 方法可以在元素输出的顺序上保证与元素进入管道流的顺序一致。也就是下面的样子(forEach 方法则无法保证这个顺序):
Monkey
Lion
Giraffe
Lemur
Lion

三、元素的收集 collect

java Stream 最常见的用法就是:一将集合类转换成管道流,二对管道流数据处理,三将管道流处理结果在转换成集合类。那么 collect()方法就为我们提供了这样的功能:将管道流处理结果在转换成集合类。

3.1. 收集为 Set

通过 Collectors.toSet()方法收集 Stream 的处理结果,将所有元素收集到 Set 集合中。


Set<String> collectToSet = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion") 
.collect(Collectors.toSet());

// 最终 collectToSet 中的元素是:[Monkey, Lion, Giraffe, Lemur],注意 Set 会去重。

3.2. 收集到 List

同样,可以将元素收集到 List 使用 toList() 收集器中。


List<String> collectToList = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion").collect(Collectors.toList());

// 最终 collectToList 中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

3.3. 通用的收集方式

上面为大家介绍的元素收集方式,都是专用的。比如使用 Collectors.toSet()收集为 Set 类型集合;使用 Collectors.toList()收集为 List 类型集合。那么,有没有一种比较通用的数据元素收集方式,将数据收集为任意的 Collection 接口子类型。
所以,这里就像大家介绍一种通用的元素收集方式,你可以将数据元素收集到任意的 Collection 类型:即向所需 Collection 类型提供构造函数的方式。


LinkedList<String> collectToCollection = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion").collect(Collectors.toCollection(LinkedList::new));

// 最终 collectToCollection 中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

注意:代码中使用了 LinkedList::new,实际是调用 LinkedList 的构造函数,将元素收集到 Linked List。当然你还可以使用诸如 LinkedHashSet::newPriorityQueue::new将数据元素收集为其他的集合类型,这样就比较通用了。

3.4. 收集到 Array

通过 toArray(String[]::new)方法收集 Stream 的处理结果,将所有元素收集到字符串数组中。


String[] toArray = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion") .toArray(String[]::new);

// 最终 toArray 字符串数组中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

3.5. 收集到 Map

使用 Collectors.toMap()方法将数据元素收集到 Map 里面,但是出现一个问题:那就是管道中的元素是作为 key,还是作为 value。我们用到了一个 Function.identity()方法,该方法很简单就是返回一个“t -> t”(输入就是输出的 lambda 表达式)。另外使用管道流处理函数 distinct() 来确保 Map 键值的唯一性。


Map<String, Integer> toMap = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.distinct()
.collect(Collectors.toMap(Function.identity(),   // 元素输入就是输出,作为 key
       s -> (int) s.chars().distinct().count()// 输入元素的不同的字母个数,作为 value));

// 最终 toMap 的结果是: {Monkey=6, Lion=4, Lemur=5, Giraffe=6}   

3.6. 分组收集 groupingBy

Collectors.groupingBy 用来实现元素的分组收集,下面的代码演示如何根据首字母将不同的数据元素收集到不同的 List,并封装为 Map。


Map<Character, List<String>> groupingByList =  Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.collect(Collectors.groupingBy(s -> s.charAt(0) ,  // 根据元素首字母分组,相同的在一组
       // counting()        // 加上这一行代码可以实现分组统计));

// 最终 groupingByList 内的元素: {G=[Giraffe], L=[Lion, Lemur, Lion], M=[Monkey]}
// 如果加上 counting(),结果是:  {G=1, L=3, M=1}

四、其他常用方法


boolean containsTwo = IntStream.of(1, 2, 3).anyMatch(i -> i == 2);
// 判断管道中是否包含 2,结果是: true

long nrOfAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur").count();
// 管道中元素数据总计结果 nrOfAnimals: 4


int sum = IntStream.of(1, 2, 3).sum();
// 管道中元素数据累加结果 sum: 6


OptionalDouble average = IntStream.of(1, 2, 3).average();
// 管道中元素数据平均值 average: OptionalDouble[2.0]



int max = IntStream.of(1, 2, 3).max().orElse(0);
// 管道中元素数据最大值 max: 3



IntSummaryStatistics statistics = IntStream.of(1, 2, 3).summaryStatistics();
// 全面的统计结果 statistics: IntSummaryStatistics{count=3, sum=6, min=1, average=2.000000, max=3}

期待您的关注

  • 博主最近新写了一本书:《手摸手教您学习 SpringBoot 系列 -16 章 97 节》
  • 本文转载注明出处(必须带连接,不能只转文字):字母哥博客。
退出移动版