使用 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

上述程式碼執行的操作可歸納如下:

  1. 使用靜態工廠方法 Stream.of(values) 建立一個包含有序 Stream 水果 String 元素的 Stream<String>

  2. 所述 filter() 操作只保留匹配給定的謂詞(元件,當由所述謂詞返回真測試),該元件。在這種情況下,它保留了包含 a 的元素。謂詞以 lambda 表示式給出。

  3. 所述 map() 操作變換使用給定的函式,稱為對映器的每個元素。在這種情況下,每個水果 String 使用方法參考 String::toUppercase對映到其大寫的 String 版本。

    請注意,如果對映函式返回與其輸入引數不同的型別,則 map() 操作將返回具有不同泛型型別的流。例如,在 Stream<String> 上呼叫 .map(String::isEmpty) 會返回一個 Stream<Boolean>

  4. 所述 sorted() 操作根據其自然順序排序 Stream 的元素(按字典順序,在 String 的情況下)。

  5. 最後, forEach(action) 操作執行一個動作,該動作作用於 Stream 的每個元素,並將其傳遞給 Consumer 。在該示例中,每個元素都被簡單地列印到控制檯。該操作是終端操作,因此不可能再次對其進行操作。

    注意由於終端操作,執行 Stream 上定義的操作。如果沒有終端操作,則不處理流。流不能重用。一旦呼叫終端操作,Stream 物件就變得不可用。

StackOverflow 文件

操作(如上所示)被連結在一起以形成可以被視為對資料的查詢。

關閉流

**請注意,通常不必關閉 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

住在 Ideone 上

並行模式允許在多個核心上使用多個執行緒,但不保證元素的處理順序。

如果在順序 Stream 上呼叫多個方法,則不必呼叫每個方法。例如,如果過濾了 Stream 並且元素的數量減少為 1,則不會發生對諸如 sort 之類的方法的後續呼叫。這可以提高順序 Stream 的效能 - 這是並行 Stream 無法實現的優化。

例:

// parallel
long howManyOddNumbersParallel = integerList.parallelStream()
                                            .filter(e -> (e % 2) == 1)
                                            .count();

System.out.println(howManyOddNumbersParallel); // Output: 2

住在 Ideone 上

與容器(或集合 )的差異

雖然可以在 Container 和 Streams 上執行某些操作,但它們最終用於不同的目的並支援不同的操作。容器更關注元素的儲存方式以及如何有效地訪問這些元素。另一方面,Stream 不能直接訪問和操作其元素; 它更專注於作為集體實體的物件組,並作為整體對該實體執行操作。StreamCollection 是針對這些不同目的的單獨的高階抽象。