Java流操作调用

14

有没有官方的Java文档可以描述Stream将为每个元素调用多少次“非干扰和无状态”的中间操作。

例如:

Arrays.asList("1", "2", "3", "4").stream()
        .filter(s -> check(s))
        .forEach(s -> System.out.println(s));

public boolean check(Object o) {
    return true;
} 

上述代码将会调用 check 方法4次。

在当前或未来版本的JDK中,从List或其他标准Java API创建的流可能比其元素数量执行 check 方法更多或更少次,这是否有可能?


4
这个问题背后有什么动机吗?比如,你想添加单元测试,例如verify(times(4)).check(anyBool),以便它在未来不会失败? - timekeeper
1
我不明白为什么那个“stream”会返回超过4个值的原因? - Mark
动机是,如果检查对于每个元素也进行重要处理,那么我希望确保它仅针对流中的每个元素执行一次。 - tsolakp
5
filter谓词应该是无状态的。” - Radiodef
你没有使用方法引用的原因是什么? - chrylis -cautiouslyoptimistic-
1
@chrylis。我认为这不会有任何影响。 - tsolakp
2个回答

15

这与流的来源无关,而是与流实现中的终端操作和优化有关。例如:

Stream.of(1,2,3,4)
      .map(x -> x + 1)
      .count();
自Java 9起,map将不会执行一次。
或者:
 someTreeSet.stream()
            .sorted()
            .findFirst();

sorted可能根本不会被执行,因为源是TreeSet并且获取第一个元素很简单,但如果这是在流API内部实现的或者不是,则是不同的问题。

所以真正的答案是 - 这取决于情况,但我无法想象一种操作会被执行多于源中元素的数量。


2
“但我想不出会执行多于源中元素数量的操作。” - 好吧,如果您为排序指定了一个比较器,它的比较肯定会被调用多次,而不仅仅是元素数量,但这只是挑剔。+1 - Fureeish
1
@Fureeish 不不,非常好的挑剔,确实你是对的;但似乎 sorted 是唯一能够做到这一点的。例如 distinct 就不能。 - Eugene
1
前段时间,我在 stackoverflow 上提出了一个关于 jdk8 flatMap 的问题(也可以参考这个相关问题)。简而言之,在 jdk8&9 中,flatMap 不完全是惰性的,元素可能会被访问多次,而在 jdk10 中,这种行为已经得到了修复。 - fps
1
正如@FedericoPeraltaSchaffner先前所说,我可以将其编写为Stream.of(1, 1).flatMap(x -> Stream.of(x, x)).forEach(System.out::println);,这是否意味着它被访问了多次? - Eugene
2
根据我的计算,这个需要翻译4次 ;) 我明白你的意思了,我在考虑使用flatMap和filter的棘手用例。在这种假设情况下,现实可能是违反直觉的。让我试着找一个例子... - fps
显示剩余9条评论

3

根据文档

寻求惰性。许多流操作,如过滤、映射或去重,可以实现惰性,从而暴露出优化的机会。例如,“查找具有三个连续元音字母的第一个字符串”不需要检查所有输入字符串。流操作分为中间(生成流)操作和终端(值或副作用生成)操作。中间操作总是惰性。

因此,由于filter是创建新Stream作为其操作的一部分的中间操作,它的惰性使得它只会调用一次过滤谓词来重建流中的每个元素。

您的方法可能在流中拥有不同数量的调用的唯一方式是如果流在状态之间以某种方式被改变,但考虑到流中的任何内容实际上仅在终端操作之前运行,这只有可能由上游错误造成。


那么在Java流中,“Laziness”是什么意思呢?对我来说,它意味着实际调用中间操作将在终端操作调用之后发生。但这并不保证中间操作将仅针对每个元素调用一次(或根据终端操作的不同可能根本不调用)。 - tsolakp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接