2016-11-29 134 views
3

我执行下面的程序不打印的Java 8流过滤器和foreach方法如预期

filter: d2 
forEach: d2 
filter: a2 
forEach: a2 
filter: b1 
forEach: b1 
filter: b3 
forEach: b3 
filter: c 
forEach: c 

不过,我期待下面的输出:

filter: d2 
filter: a2 
filter: b1 
filter: b3 
filter: c 
forEach: d2 
forEach: a2 
forEach: b1 
forEach: b3 
forEach: c 

意思是,首先filter方法循环应该已经完全执行,然后方法循环应该已经开始。

有什么我做错了吗?

+2

你没有做错什么,这是预期。 – Tunaki

+1

相关:http://stackoverflow.com/questions/32414088/java-8-stream-difference-between-limit-and-skip/32414480 –

+2

此外你的心智模式假设你需要通过所有的元素管道在执行终端操作之前。如果您需要找到满足谓词的第一个元素,那么这将会非常低效。 –

回答

7

你的期望是错的。

执行终端操作forEach时,它一次只消耗Stream中的一个元素。它消耗的每个元素必须通过Stream的所有中间操作,这导致filterforEach之前的元素上执行,该元素在同一元素上执行(假定元素通过filter)。

换句话说,filter被懒惰地应用于只是当在Stream流水线中的下一操作需要它的下一个元素(在你的情况下一个操作是forEach)的每个元素。

这意味着,如果在终端的操作将仅需要一些流元素,直到所述第一元件将其传递(待处理(例如,如果取代forEach()findFirst()),则filter()操作将只被执行,在你的例子意味着过滤器只会被执行第一个元素)。

1

流的处理将从终端操作开始,并消耗一个接一个的项目。其优点是:

该方案具有计算只有相关项目 - 例如,如果只需要一个项目,该filter只调用一次:

Stream.of("d2", "a2", "b1", "b3", "c") 
.filter(s -> { 
    System.out.println("filter: " + s); 
    return true; 
}) 
.findAny() 
.ifPresent(s -> System.out.println("forEach: " + s)); 

输出:

filter: d2 
forEach: d2 

你甚至可以创建infinte流(否则将不可能):

IntStream.iterate(0, i -> i+1) 
.filter(s -> { 
    System.out.println("filter: " + s); 
    return true; 
}) 
.peek(i -> { 
    try { 
     Thread.sleep(1000); 
    } catch (InterruptedException e) { 
    } 
}) 
.forEach(s -> System.out.println("forEach: " + s)); 

输出:

filter: 0 
forEach: 0 
filter: 1 
forEach: 1 
... (up to infinity)