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 相關文檔 。
閱讀更多 小技術君 的文章