作者:知秋
來源:重走Java基礎之Streams(一)
因為經常逛stackoverflow
,最近也在看reactive和storm以及前一陣子也用流式ORM框架speedment
與Springboot
整合改造了migo2.0中的單點登錄,深深的感受到java8已經融入我們很深了,尤其是Spring5對其進行大力支持,覺得有必要再對自己的知識整理一下,順帶就把stackoverflow
一些東西自己拿過來整理翻譯一下,里面也會加入一些自己的理解
Introduction
流表示一系列元素并支持不同類型的操作來對這些元素執行計算。在Java 8中,Collection接口有兩種方法來生成Stream
- 1)stream()和
- 2) parallelStream()
流操作包括中間或終端。 中間操作返回一個流,所以我們可以鏈接多個中間操作而不使用分號。 終端操作是void的或返回非流結果。
Examples
Using Streams
A Stream
是可以執行順序和并行聚合操作的一系列元素 。 任何給定的“Stream”都可能有無限量的數據流過它。 你所得到的結果是從“Stream”接收的數據在到達時被單獨處理,而不是完全對數據執行批處理。 當與lambda表達式 結合時,它們提供了使用函數方法對數據序列執行操作的簡明方法。
Example: (see it work on Ideone)
Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");
fruitStream.filter(s -> s.contains("a"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
Output:
APPLE
BANANA
ORANGE
PEAR
上述代碼執行的操作可以總結如下:
使用靜態工廠方法
Stream.of(values)
創建一個包含fruitString
的順序排序Stream
的Stream
元素filter()
操作僅保留與給定謂詞(由謂詞返回true測試時的元素)匹配的元素。 在這種情況下,它保留含有“a”的元素。 謂詞作為lambda表達式給出。-
map()
操作轉換 每個元素使用給定的函數,稱為映射器。 在這種情況下,每個fruitString
使用method-reference映射到將string字符串轉換為大寫版本String::toUppercase
。Note 如果映射函數返回與其輸入參數不同的類型,那么
map()
操作將返回具有不同泛型類型的流。 例如在一個Stream
調用.map(String :: isEmpty)
返回一個Stream<Boolean>
sorted()
操作對Stream
的元素進行排序 根據它們的自然排序(根據在'String'的情況下對所在字典的順序,其實都知道)。最后,
forEach(action)
操作執行一個動作,作用于“Stream”的每個元素,將其傳遞給一個 Consumer。 在該示例中,每個元素只是被打印到控制臺。 該操作是終端操作,因此不可能再次進行操作。Note 在
Stream
中定義的操作之所以被執行,是因為最后有終端操作。 假如沒有終端操作,'Stream'將不被處理,因為'Stream'輸出不被任何終端操作使用(省的浪費計算資源,所以很多書上稱之為被動式foreach)。
操作(如上所示)鏈接在一起以形成可以被視為對數據的查詢
Reusing Streams
一個Stream
不能重復使用。 一旦調用任何中間或終端操作,“Stream”對象將變得不可用。 Stream
代替地使用中間Stream
對象以便將中間操作鏈接在一起通過一系列Stream
操作來生成一個Stream
對象作為中間對象,最后再調用這個生成的Stream
對象來完成最終的操作,最后一步的操作只能進行一次,之后,此流已經沒了(生命周期已結束)。
Example:
Stream<String> stream =
Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
stream.anyMatch(s -> true); // The Stream has been used and is now consumed.
stream.noneMatch(s -> true); // IllegalStateException; stream was already used
Closing Streams
Stream
接口擴展了 AutoCloseable
。Streams可以通過調用 close
方法或使用try-with -resource語句來關閉。
請注意,Stream通常不必關閉。僅需要關閉在IO通道上運行的流。 大多數
Stream
型不對資源操作,因此不需要關閉。
Stream
應該關閉的示例用例是,當您從文件創建一個Stream
行時:
try(final Stream<String> lines = Files.lines(Paths.get("somePath"))){
lines.forEach(System.out::println);
}
Stream
接口也聲明了Stream.onClose()
方法,它允許你注冊 Runnable
處理程序,當 流關閉。 一個示例用例是產生流的代碼需要知道它何時被消耗以執行一些清理。
public Stream<String>streamAndDelete(Path path) throws IOException {
return Files.lines(path)
.onClose(()->someClass.deletePath(path));
}
運行處理程序只有在調用close()
方法時才會執行,例如通過try-with-resources:
Path myPath = Paths.get("somePath");
try(final Stream<String> lines = streamAndDelete(myPath)){
lines.forEach(System.out::println);
}
Files.exists(myPath); // returns false
If close() isn't called, explicitly or implicitly, then the handler will not be called either:
如果沒有明確或隱式地調用close()
,那么處理程序不會被調用:
streamAndDelete(myPath)
.forEach(System.out::println);
Files.exists(myPath); // returns true
Processing Order
Stream
對象的處理可以是順序或 parallel(并行)。
在** sequential **模式中,按照“Stream”的源的順序處理元素。 如果Stream
是有序的(例如 SortedMap
實現或List
,處理過程保證匹配源的排序。 然而,在其他情況下,應注意不要依賴于順序(參見:是Java的HashMap`` keySet()
迭代順序一致?)。
Example:
List<Integer> integerList = Arrays.asList(0, 1, 2, 3, 42);
// sequential
long howManyOddNumbers = integerList.stream()
.filter(e -> (e % 2) == 1).count();
System.out.println(howManyOddNumbers); // Output: 2
并行模式允許在多個核上使用多個線程,但不能保證處理元素的順序。
如果在順序的 Stream
上雖然調用了多個方法,則不一定必須要調用每個方法。 例如,如果一個 Stream
被過濾,并且元素的數量減少到一,則不會發生對諸如sort
的方法的后續調用。 這可以提高順序的Stream
的性能 - 這是一個并行的Stream
不可能實現的優化。
Example:
// parallel
long howManyOddNumbersParallel = integerList.parallelStream()
.filter(e -> (e % 2) == 1).count();
System.out.println(howManyOddNumbersParallel); // Output: 2