使用 Streams
一個 Stream
是在其上順序和並行聚合操作可以執行元素的序列。任何給定的 Stream
都可能有無限量的資料流過它。結果,從 Stream
接收的資料在到達時被單獨處理,而不是完全對資料進行批處理。當與 lambda 表示式結合使用時,它們提供了一種使用函式方法對資料序列執行操作的簡明方法。
例如: ( 看到它在 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);
輸出:
蘋果
香蕉
ORANGE
PEAR
上述程式碼執行的操作可歸納如下:
-
使用靜態工廠方法
Stream.of(values)
建立一個包含有序Stream
水果String
元素的Stream<String>
。 -
所述
filter()
操作只保留匹配給定的謂詞(元件,當由所述謂詞返回真測試),該元件。在這種情況下,它保留了包含a
的元素。謂詞以 lambda 表示式給出。 -
所述
map()
操作變換使用給定的函式,稱為對映器的每個元素。在這種情況下,每個水果String
使用方法參考String::toUppercase
對映到其大寫的String
版本。請注意,如果對映函式返回與其輸入引數不同的型別,則
map()
操作將返回具有不同泛型型別的流。例如,在Stream<String>
上呼叫.map(String::isEmpty)
會返回一個Stream<Boolean>
-
所述
sorted()
操作根據其自然順序排序Stream
的元素(按字典順序,在String
的情況下)。 -
最後,
forEach(action)
操作執行一個動作,該動作作用於Stream
的每個元素,並將其傳遞給 Consumer 。在該示例中,每個元素都被簡單地列印到控制檯。該操作是終端操作,因此不可能再次對其進行操作。注意,由於終端操作,執行
Stream
上定義的操作。如果沒有終端操作,則不處理流。流不能重用。一旦呼叫終端操作,Stream
物件就變得不可用。
操作(如上所示)被連結在一起以形成可以被視為對資料的查詢。
關閉流
**請注意,通常不必關閉
Stream
。**只需要關閉在 IO 通道上執行的流。大多數Stream
型別不依賴於資源,因此不需要關閉。
Stream
介面擴充套件了 AutoCloseable
。可以通過呼叫 close
方法或使用 try-with-resource 語句來關閉流。
應該關閉 Stream
的示例用例是當你從檔案建立線條時:
try (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));
}
只有在 try-with-resources 語句顯式或隱式呼叫 close()
方法時,才會執行執行處理程式。
處理訂單
Stream
物件的處理可以是順序的或並行的 。
在順序模式中,元素按照 Stream
的來源順序處理。如果訂購了 Stream
(例如 SortedMap
實現或 List
),則保證處理與源的順序相匹配。但是,在其他情況下,應注意不要依賴於排序(參見: Java HashMap
keySet()
迭代順序是否一致? )。
例:
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
並且元素的數量減少為 1,則不會發生對諸如 sort
之類的方法的後續呼叫。這可以提高順序 Stream
的效能 - 這是並行 Stream
無法實現的優化。
例:
// parallel
long howManyOddNumbersParallel = integerList.parallelStream()
.filter(e -> (e % 2) == 1)
.count();
System.out.println(howManyOddNumbersParallel); // Output: 2
與容器(或集合 )的差異
雖然可以在 Container 和 Streams 上執行某些操作,但它們最終用於不同的目的並支援不同的操作。容器更關注元素的儲存方式以及如何有效地訪問這些元素。另一方面,Stream
不能直接訪問和操作其元素; 它更專注於作為集體實體的物件組,並作為整體對該實體執行操作。Stream
和 Collection
是針對這些不同目的的單獨的高階抽象。