Java flatMap保证是懒惰的吗?

Java flatMap保证是懒惰的吗?,java,java-8,java-stream,flatmap,Java,Java 8,Java Stream,Flatmap,考虑以下代码: urls.stream() .flatMap(url -> fetchDataFromInternet(url).stream()) .filter(...) .findFirst() .get(); 当第一个url足够时,是否会为第二个url调用fetchDataFromInternet 我尝试了一个较小的例子,它看起来像预期的那样工作。i、 e逐个处理数据,但这种行为是否可靠?如果没有,在.flatMap(…)之前调用.sequenti

考虑以下代码:

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

当第一个url足够时,是否会为第二个url调用
fetchDataFromInternet

我尝试了一个较小的例子,它看起来像预期的那样工作。i、 e逐个处理数据,但这种行为是否可靠?如果没有,在
.flatMap(…)
之前调用
.sequential()
是否有帮助

    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部分是懒惰的。i、 e完全读取第一个流,并且只有在需要时,它才会进入下一个流。读取一个流是渴望的,但读取多个流是懒惰的

如果此行为是有意的,API应该让函数返回一个
Iterable
,而不是流

换句话说:

在当前的实现下,
flatmap
是渴望的;与任何其他有状态的中间操作一样(如
排序的
不同的
)。很容易证明:

 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)
生成
10000
行,那么您的
findFirst
将不得不等待所有10000行被计算,即使您只关心其中一行

编辑

这在Java10中是固定的,我们从中恢复了懒惰:参见

编辑2


这在Java8(8u222)中也得到了修复:

不清楚为什么您设置的示例没有解决您感兴趣的实际问题。如果您想知道在应用短路操作(如
findFirst()
)时处理是否延迟,那么请使用一个示例,使用
findFirst()
而不是处理所有元素的
forEach
。另外,将logging语句放在要跟踪其求值的函数中:

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函数为“hello”求值
处理元件h
处理元件e
处理元件l
处理元件l
处理元件o
找到一个l
这表明传递给
flatMap
的函数会像预期的那样延迟求值,而返回(子)流的元素不会像您链接自己时所讨论的那样延迟求值


因此,对于从传递给
flatMap
的函数调用的
fetchDataFromInternet
方法,您将获得所需的惰性。但不是因为它返回的数据。

今天我也偶然发现了这个错误。行为并并没有那个么直接,因为下面这样的简单案例可以很好地工作,但类似的生产代码不起作用

 stream(spliterator).map(o -> o).flatMap(Stream::of)..flatMap(Stream::of).findAny()
对于那些不能再等几年迁移到JDK-10的人来说,有一个真正的懒惰流。它不支持并行。它专门用于JavaScript翻译,但对我来说效果不错,因为接口是一样的

StreamHelper是基于集合的,但它很容易适应拆分器


上的文档说“当您创建流时,除非另有规定,否则它始终是串行流”。因此,不需要调用
.sequential()
。您认为它不是什么?@pedromss文档没有明确说明。看起来很少有不懒惰的情况:@balki在公认的答案中链接了post-you的状态,即中间操作总是懒惰的。此外,来自:“流是惰性的;只有在终端操作启动时才对源数据执行计算,并且源元素仅在需要时使用。”Flatmap是一种中间操作
fetchDataFromInternet
不会被调用过多,但是,通过特定的
fetchDataFromInternet
调用返回的元素可能会得到处理而不会延迟。看起来Java 8也是如此。@ZhekaKozlov感谢您提供的信息-如果您愿意,也可以编辑答案。