了解java 8 stream的过滤方法

时间:2014-11-01 09:39:34

标签: java filter java-8 java-stream

我最近在Java 8中了解了Stream并看到了这个例子:

IntStream stream = IntStream.range(1, 20);

现在,让我们说我们想要找到第一个可分为3和5的数字。我们可能filter两次findFirst如下:

OptionalInt result = stream.filter(x -> x % 3 == 0)
                               .filter(x -> x % 5 == 0)
                               .findFirst();

这听起来很合理。当我尝试这样做时出现了令人惊讶的部分:

OptionalInt result = stream.filter(x -> {System.out.println(x); return x % 3 == 0;})
                           .filter(x -> {System.out.println(x); return x % 5 == 0;})
                           .findFirst();

System.out.println(result.getAsInt());

我希望得到类似:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20然后:3 6 9 12 15 18的内容。因为我们首先遍历1到20之间的所有数字,只过滤那些可被3除数的数字,然后迭代这个新的Stream并找到可被5除的那些。

但我得到了这个输出:1 2 3 3 4 5 6 6 7 8 9 9 10 11 12 12 13 14 15 15 15

看起来它并没有覆盖所有数字。此外,看起来它仅检查x % 5 == 0那些可被3分割的数字。

我不明白为什么它不会遍历所有数字。

以下是代码的在线摘录:http://www.tryjava8.com/app/snippets/5454a7f2e4b070922a64002b

2 个答案:

答案 0 :(得分:8)

嗯,关于流的理解是,与列表不同,它们不一定(必然)保存所有项目,而是一次计算每个项目(懒惰评估)。

这意味着当您执行IntStream stream = IntStream.range(1, 20);时,您实际上并未创建包含20个项目的集合。您创建了一个动态计算的集合。每次调用此流next都会计算下一个项目。其余的项目仍然“不存在”(有点说)。

过滤器也一样。

当您添加检查除以3的过滤器时,您将获得一个由2个计算组合而成的新流 - 第一个返回1中的数字直到达到20,第二个计算返回除以的数字3.了解每次只计算第一项是很重要的。这就是为什么当你添加除以5的检查时它只能处理那些可被3整除的项目。同样是为什么打印在15处停止。findFirst方法返回通过所有3个计算的第一个数字(1-20范围计算,3计算除法和5计算除法)。

答案 1 :(得分:3)

Stream是一种有效处理集合的惰性评估机制。这意味着除非最终(终端)操作需要,否则不会评估Stream上的所有中间操作。

在您的示例中,终端操作为firstFirst()。这意味着Stream将评估中间操作的管道,直到找到通过将输入Stream传递给所有中间操作而产生的单个int。

第二个过滤器只接收通过第一个过滤器的int,所以它只处理数字3,6,9,12,15然后停止,因为15通过过滤器,并提供findFirst()操作只输出它需要。

只要终端操作仍然需要数据,第一个过滤器将只处理输入流的整数,因此它只处理1到15个。