2017-09-18 29 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方法,您将获得所需的懒惰。但不是它返回的数据。