2017-09-18 74 views
4

考慮下面的代碼:flatMap是否保證懶惰?

urls.stream() 
    .flatMap(url -> fetchDataFromInternet(url).stream()) 
    .filter(...) 
    .findFirst() 
    .get(); 

fetchDataFromInternet堪稱爲第二網址時,第一個是夠嗎?

我嘗試了一個較小的例子,它看起來像按預期工作。即逐個處理數據,但這種行爲是否可以依賴?如果沒有,請撥打.sequential()之前.flatMap(...)幫忙嗎?

Stream.of("one", "two", "three") 
      .flatMap(num -> { 
       System.out.println("Processing " + num); 
       // return FetchFromInternetForNum(num).data().stream(); 
       return Stream.of(num); 
      }) 
      .peek(num -> System.out.println("Peek before filter: "+ num)) 
      .filter(num -> num.length() > 0) 
      .peek(num -> System.out.println("Peek after filter: "+ num)) 
      .forEach(num -> { 
       System.out.println("Done " + num); 
      }); 

輸出:

Processing one 
Peek before filter: one 
Peek after filter: one 
Done one 
Processing two 
Peek before filter: two 
Peek after filter: two 
Done two 
Processing three 
Peek before filter: three 
Peek after filter: three 
Done three 

更新:使用Oracle官方JDK8如果該事項在實施

:根據意見和下面的答案 ,flatmap是部分懶。即完全讀取第一個數據流,並且只在需要時讀取,接下來進行。閱讀流是急切的,但閱讀多個流是懶惰的。

如果這種行爲是有意的,那麼API應該讓該函數返回一個Iterable而不是一個流。

換句話說:link

+2

關於[並行性](https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html)上的文檔說「創建流時,除非另有說明,否則它始終是串行流。 「,所以調用'.sequential()'是沒有必要的。 – teppic

+0

是什麼讓你覺得它不是? – pedromss

+0

@pedromss文檔沒有明確說明。 https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#flatMap-java.util.function.Function- 而且看起來好像有幾種情況可能不會是懶惰的:https://stackoverflow.com/questions/29229373/why-filter-after-flatmap-is-not-completely-lazy-in-java-streams – balki

回答

7

在當前實施flatmap渴望;像任何其他有狀態的中間操作(如sorteddistinct)。而且這很容易證明:

int result = Stream.of(1) 
      .flatMap(x -> Stream.generate(() -> ThreadLocalRandom.current().nextInt())) 
      .findFirst() 
      .get(); 

    System.out.println(result); 

這永遠不會完成,因爲flatMap是熱切計算。對於你的例子:

urls.stream() 
    .flatMap(url -> fetchDataFromInternet(url).stream()) 
    .filter(...) 
    .findFirst() 
    .get(); 

這意味着每url,在flatMap將阻止其他所有操作後,該來的,即使你在乎一個。因此,我們假設從一個url您的fetchDataFromInternet(url)生成10_000行,以及您的findFirst將不得不等待所有 10_000來計算,即使你只關心一個。

5

目前尚不清楚爲什麼你設置了一個不能解決實際問題的例子,你對此感興趣。如果你想知道,當應用像findFirst()這樣的短路操作時,處理是否是懶惰的,然後使用一個使用findFirst()而不是forEach的例子來處理所有元素。此外,把日誌語句直接進入你想跟蹤其評價功能:

Stream.of("hello", "world") 
     .flatMap(s -> { 
      System.out.println("flatMap function evaluated for \""+s+'"'); 
      return s.chars().boxed(); 
     }) 
     .peek(c -> System.out.printf("processing element %c%n", c)) 
     .filter(c -> c>'h') 
     .findFirst() 
     .ifPresent(c -> System.out.printf("found an %c%n", c)); 
flatMap function evaluated for "hello" 
processing element h 
processing element e 
processing element l 
processing element l 
processing element o 
found an l 

這表明,隨着預期傳遞給flatMap功能被懶洋洋地評估而返回的(子)的元素流不會被評估爲儘可能懶惰,正如已在the Q&A中討論的那樣,您已將自己鏈接起來。

因此,關於從傳遞給flatMap的函數調用的fetchDataFromInternet方法,您將獲得所需的懶惰。但不是它返回的數據。