Java8 新特性 —— Stream 流式程式設計
阿新 • • 發佈:2020-11-14
> 本文部分摘自 On Java 8
## 流概述 集合優化了物件的儲存,大多數情況下,我們將物件儲存在集合是為了處理他們。使用流可以幫助我們處理物件,無需迭代集合中的元素,即可直接提取和操作元素,並添加了很多便利的操作,例如查詢、過濾、分組、排序等一系列操作。 流的一個核心好處是:它使得程式更加短小並且易於理解,當結合 Lambda 表示式和方法引用時,會讓人感覺自成一體。總而言之,流就是一種高效且易於使用的處理資料的方式。 觀察下面的例子: ```java public class Randoms { public static void main(String[] args) { new Random(47) // 建立 Random 物件,並給一個種子 .ints(5, 20) // 產生一個限定了邊界的隨機整數流 .distinct() // 使流中的整數不重複 .limit(7) // 取前7個元素 .sorted() // 排序 .forEach(System.out::println); // 根據傳遞給它的函式對流中每個物件執行操作 } } ``` 通過上面的示例,我們可以發現流有如下特點: 1. 流本身不儲存元素,並且不會改變源物件,相反,它會返回一個持有結果的新流 2. 流可以在不使用賦值或可變資料的情況下對有狀態的系統建模 3. 流是一種宣告式程式設計風格,它宣告想要做什麼,而非指明如何做 4. 流的迭代過稱為內部迭代,你看不到迭代過程,可讀性更強 5. 流是懶載入的,它會等到需要時才執行
## Optional 類 如果在一個空流中嘗試獲取元素,結果肯定是得到一個異常。我們希望可以得到友好的提示,而不是糊你一臉 NullPointException。Optional 的出現就是為了解決臭名昭著的空指標異常 一些標準流操作返回 Optional 物件,因為它們不能保證預期結果一定存在,包括: - `findFirst()` 返回一個包含第一個元素的 Optional 物件,如果流為空則返回 Optional.empty - `findAny()` 返回包含任意元素的 Optional 物件,如果流為空則返回 Optional.empty - `max()` 和 `min()` 返回一個包含最大值或者最小值的 Optional 物件,如果流為空則返回 Optional.empty - `reduce(Function)` 將函式的返回值包裝在 Optional 中 #### 1. 便利函式 Optional 類本質上是一個容器物件,所謂容器是指:它可以儲存型別 T 的值,也可以儲存一個 null。此外,Optional 提供了許多有用的方法,可以幫助我們不用顯示地進行空值檢測: - `ifPresent()` 是否有值存在,存在放回 true,否則返回 false - `ifPresent(Consumer)` 當值存在時呼叫 Consumer,否則什麼也不做 - `orElse(otherObject)` 如果值存在則直接返回,否則生成 otherObject - `orElseGet(Supplier)` 如果值存在則直接返回,否則使用 Supplier 函式生成一個可替代物件 - `orElseThrow(Supplier)` 如果值存在則直接返回,否則使用 Supplier 函式生成一個異常 下面是對 Optional 的一個簡單應用 ```java class OptionalBasics { static void test(Optional
## 終端操作 終端操作將獲取流的最終結果,至此我們無法再繼續往後傳遞流。可以說,終端操作總是我們在使用流時所做的最後一件事 #### 1. 陣列 當我們需要得到陣列型別的資料以便於後續操作時,可以使用下述方法產生陣列: - `toArray()` 將流轉換成適當型別的陣列 - `toArray(generetor)` 生成自定義型別的陣列 #### 2. 迴圈 常見的如 `forEach(Consumer)`,另外還有 `forEachOrdered(Consumer)`,保證按照原始流的順序操作。第二種形式僅在引入並行流時才有意義。所謂並行流是將流分割為多個,並在不同的處理器上分別執行。由於多處理器並行操作的原因,輸出的結果可能會不一樣,因此需要用到 `forEachOrdered(Consumer)` #### 3. 集合 在這裡我們只是簡單介紹一下常見的 Collectors 示例,實際上它還有一些非常複雜的實現。大多數情況下,`java.util.stream.Collectors` 中預設的 Collector 就能滿足我們的需求 - `collect(Collector)` 使用 Collector 收集流元素到結果集合中 - `collect(Supplier, BiConsumer, BiConsumer)` 第一個引數建立一個新的結果集合,第二個引數將下一個元素收集到結果集合中,第三個引數用於將兩個結果集合合併起來 #### 4. 組合 組合意味著將流中所有元素以某種方式組合為一個元素 - `reduce(BinaryOperator)` 使用 BinaryOperator 來組合所有流中的元素。因為流可能為空,其返回值為 Optional - `reduce(identity, BinaryOperator)` 功能同上,但是使用 identity 作為其組合的初始值。因此如果流為空,identity 就是結果 看一段程式碼示例: ```java Stream.generate(Math::random).limit(10) .reduce((fr0, fr1) -> fr0.size < 50 ? fr0 : fr1).ifPresent(System.out::println); ``` 返回的結果是 Optional 型別,Lambda 表示式中的第一個引數 fr0 是 reduce 中上一次呼叫的結果,而第二個引數 fr1 是從流傳遞過來的值 #### 5. 匹配 `allMatch(Predicate)` 如果流的每個元素提供給 Predicate 都返回 true ,結果返回為 true。在第一個 false 時,則停止執行計算 `anyMatch(Predicate)` 如果流的任意一個元素提供給 Predicate 返回 true ,結果返回為 true。在第一個 true 是停止執行計算 `noneMatch(Predicate)` 如果流的每個元素提供給 Predicate 都返回 false 時,結果返回為 true。在第一個 true 時停止執行計算 #### 6. 查詢 `findFirst()` 返回第一個流元素的 Optional,如果流為空返回 Optional.empty `findAny(` 返回含有任意流元素的 Optional,如果流為空返回 Optional.empty #### 7. 資訊 `count()` 流中的元素個數 `max(Comparator)` 根據所傳入的 Comparator 所決定的最大元素 `min(Comparator)` 根據所傳入的 Comparator 所決定的最小元素 #### 8. 數字流資訊 `average()` 求取流元素平均值 `max()` 和 `min()` 數值流操作無需 Comparator `sum()` 對所有流元素進行求