深入理解Java函数式编程系列第3部分Java8的Lambda表达式和流处理

Stream.of(1, 2, 3).reduce((v1, v2) -> v1 + v2)

.ifPresent(System.out::println);

// 输出 6

int result1 = Stream.of(1, 2, 3, 4, 5)

.reduce(1, (v1, v2) -> v1 * v2);

System.out.println(result1);

// 输出 120

int result2 = Stream.of(1, 2, 3, 4, 5)

.parallel()

.reduce(0, (v1, v2) -> v1 + v2, (v1, v2) -> v1 + v2);

System.out.println(result2);

// 输出 15


Max 和 min 是两种特殊的约简操作,分别求得流中元素的最大值和最小值。

对于一个流,操作 allMatch、anyMatch 和 nonMatch 分别用来检查是否流中的全部元素、任意元素或没有元素满足给定的条件。判断的条件由 Predicate 指定。

操作 findFirst 和 findAny 分别查找流中的第一个或任意一个元素。两个方法的返回值都是 Optional 对象。当流为空时,返回的是空的 Optional 对象。如果一个流没有确定的相遇顺序,那么 findFirst 和 findAny 的行为在本质上是相同的。

操作 collect 表示的是另外一类的约简操作。与 reduce 不同在于,collect 会把结果收集到可变的容器中,如 List 或 Set。收集操作通过接口 java.util.stream.Collector 来实现。Java 已经在类 Collectors 中提供了很多常用的 Collector 实现。

第一类收集操作是收集到集合中,常见的方法有 toList()、toSet() 和 toMap() 等。第二类收集操作是分组收集,即使用 groupingBy 对流中元素进行分组。分组时对流中所有元素应用同一个 Function。具有相同结果的元素被分到同一组。分组之后的结果是一个 Map,Map 的键是应用 Function 之后的结果,而对应的值是属于该组的所有元素的 List。在清单 9 中,流中的元素按照字符串的第一个字母分组,所得到的 Map 中的键是 A、B 和 D,而 A 对应的 List 值中包含了 Alex 和 Amy 两个元素,B 和 D 所对应的 List 值则只包含一个元素。

清单 9. 收集器 groupingBy 示 例


final Map<character>> names = Stream.of("Alex", "Bob", "David", "Amy")/<character>

.collect(Collectors.groupingBy(v -> v.charAt(0)));

System.out.println(names);


第三类的 joining 操作只对元素类型为 CharSequence 的流使用,其作用是把流中的字符串连接起来。清单 10 中把字符串流用", "进行连接。

清单 10. 收集器 joining 示 例

String str = Stream.of("a", "b", "c")

.collect(Collectors.joining(", "));

System.out.println(str);


第四类的 partitioningBy 操作的作用类似于 groupingBy,只不过分组时使用的是 Predicate,也就是说元素最多分成两组。所得到结果的 Map 的键的类型是 Boolean,而值的类型同样是 List。

还有一些收集器可以进行数学计算,不过只对元素类型为 int、long 或 double 的流可用。这些数学计算包括:

  • averagingDouble、averagingInt 和 averagingLong 计算流中元素的平均值。
  • summingDouble、summingInt 和 summingLong 计算流中元素的和。
  • summarizingDouble、summarizingInt 和 summarizingLong 对流中元素进行数学统计,可以得到平均值、数量、和、最大值和最小值。

清单 11 展示了这些数学计算相关的收集器的用法。

清单 11. 与数学计算相关的收集器


double avgLength = Stream.of("hello", "world", "a")

.collect(Collectors.averagingInt(String::length));

System.out.println(avgLength);

final IntSummaryStatistics statistics = Stream.of("a", "b", "cd")

.collect(Collectors.summarizingInt(String::length));

System.out.println(statistics.getAverage());

System.out.println(statistics.getCount());


Stream 中还有其他实用的操作,限于篇幅不能全部介绍。相关的用法可以查看 API 文档。

总结

Java 8 引入的 Lambda 表达式和流处理是可以极大提高开发效率的重要特性。每个 Java 开发人员都应该熟练掌握它们的使用。本文从 JSR 335 出发对 Lambda 表达式进行了深入的介绍,同时也对流的特征和操作进行了详细说明。下一篇文章将对 Java 平台上流行的函数式编程库 Vavr 进行介绍。

参考资源

  • 参考关于 Lambda 表达式的 JSR 335 的内容。
  • 查看 JDK 10 中 Stream 的相关文档 。
  • 查看 JDK 10 中的 Spliterator 相关文档 。


分享到:


相關文章: