了解java 8 stream的过滤方法

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

 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

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

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

filter也是如此。

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

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

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

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

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