消耗流

一个 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()