2016-11-29 139 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)