消耗流
一个 Stream
当存在将仅被遍历终端操作,像 count()
, collect()
或 forEach()
。否则,将不会对 Stream
执行任何操作。
在下面的示例中,没有向 Stream
添加终端操作,因此不会调用 filter()
操作,也不会产生输出,因为 peek()
不是终端操作。
IntStream.range(1, 10).filter(a -> a % 2 == 0).peek(System.out::println);
这是具有有效终端操作的 Stream
序列,因此产生输出。
你也可以使用 forEach
而不是 peek
:
IntStream.range(1, 10).filter(a -> a % 2 == 0).forEach(System.out::println);
输出:
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()");
}
输出:
我们得到一个 NullPointerException,因为 null 作为参数传递给
filter()