深入理解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 相關文檔 。


分享到:


相關文章: