消耗流

一個 Stream 當存在將僅被遍歷終端操作,像 count()collect()forEach() 。否則,將不會對 Stream 執行任何操作。

在下面的示例中,沒有向 Stream 新增終端操作,因此不會呼叫 filter() 操作,也不會產生輸出,因為 peek() 不是終端操作

IntStream.range(1, 10).filter(a -> a % 2 == 0).peek(System.out::println);

住在 Ideone 上

這是具有有效終端操作Stream 序列,因此產生輸出。

你也可以使用 forEach 而不是 peek

IntStream.range(1, 10).filter(a -> a % 2 == 0).forEach(System.out::println); 

住在 Ideone 上

輸出:

2
4
6
8

在執行終端操作之後,Stream 被消耗並且不能被重用。

雖然給定的流物件無法重用,但很容易建立一個可重用的 Iterable,它委託給一個流管道。這對於返回實時資料集的修改檢視而不必將結果收集到臨時結構中非常有用。

List<String> list = Arrays.asList("FOO", "BAR");
Iterable<String> iterable = () -> list.stream().map(String::toLowerCase).iterator();

for (String str : iterable) {
    System.out.println(str);
}
for (String str : iterable) {
    System.out.println(str);
}

輸出:

foo
bar
foo
bar

這是因為 Iterable 宣告瞭一個抽象方法 Iterator<T> iterator()。這使它成為一個功能介面,由 lambda 實現,在每次呼叫時建立一個新流。

通常,Stream 的執行方式如下圖所示:

https://i.stack.imgur.com/lrwjM.jpg

注意 :即使沒有終端操作,也始終執行引數檢查 :

try {
    IntStream.range(1, 10).filter(null);
} catch (NullPointerException e) {
    System.out.println("We got a NullPointerException as null was passed as an argument to filter()");
}

住在 Ideone 上

輸出:

我們得到一個 NullPointerException,因為 null 作為引數傳遞給 filter()