Java 8和Java 9中的Stream.peek()方法

75

我正在学习Java 8的 lambda 表达式,并想问一下我遇到的涉及 peek 方法的函数接口的以下 Java 代码片段。

在 IDE 上执行程序时,它没有输出。我本来以为它会输出 2, 4, 6

import java.util.Arrays;
import java.util.List;

public class Test_Q3 {

    public Test_Q3() {
    }

    public static void main(String[] args) {
        List<Integer> values = Arrays.asList(1, 2, 3);
        values.stream()
              .map(n -> n * 2)
              .peek(System.out::print)
              .count();
    }
}

4
或许应该将java-8标签替换为java-9。 - Eran
9
已添加Java 9。我认为应该是两者都包括,因为它涉及到两种的不同之处。 - eis
9
学习Java8和Java9之间的好处有哪些?概括这个问题的标题。另外,Holger 在这个答案中已经很全面地介绍了这个问题。 - Naman
2个回答

68

我假设你是在Java 9下运行这个程序?你没有改变流的属性,因此根本没有必要执行或。

换句话说,你只关心最终结果count,但同时你并没有以filterdistinct等方式改变List的初始大小。这是流中进行的一种优化。

顺便说一句,即使你添加一个虚拟过滤器也会显示你期望的结果:

values.stream ()
      .map(n -> n*2)
      .peek(System.out::print)
      .filter(x -> true)
      .count();

20
现在想象一下未来的版本,它能够分析“Predicate”代码以预测其结果,即对于特定的流管道,它是否永远无法或总是满足。 - Holger
4
因此,这则寓言的寓意是不要使用count来强制执行流以达到副作用的目的。 - puhlen
29
不,道德是,不要使用 peek 作为强制流执行以产生副作用的方式(除了调试之外)。如果您想对每个流元素应用操作,请使用 forEach - Holger
2
@klaar “一条链完成”并不是一个实际的目标。当你首先收集到一个列表中,然后通过对该结果列表执行forEach来执行操作时,你并没有注意到任何区别。除此之外,在这个问答中已经展示了为什么peek根本不是一个替代品,所以说“没有peek的替代方案”是没有意义的。 - Holger
3
如@klaar所说,“尝试将现有代码适配成一条Java流调用链”是一个人为设定的目标。 即使重写代码以使用流也不是一个有用的目标,特别是当这个重写甚至不能产生清晰的代码时。 正如已经说过的,您根本不需要peek,因为在结果集上进行后续检查时没有任何区别。 特别是,这样的后续测试可以抛出这些受检异常,而peek不会。 - Holger
显示剩余4条评论

65

以下是《Stream》接口Javadoc中相关的引用:

流实现在优化结果计算时有很大的自由度。例如,如果能够证明它不会影响计算结果,则流实现可以省略流水线中的操作(或整个阶段)-- 因此省略行为参数的调用。这意味着行为参数的副作用可能并非总是被执行,除非另有规定(例如通过终端操作forEach和forEachOrdered)。 (有关此类优化的具体示例,请参见文档计数(count())操作上记录的API注释。有关更多详细信息,请参见流软件包文档的副作用部分。)

更具体地来说,以下是《count()》方法的Javadoc:

API注意:

如果能够直接从流源计算出计数,则实现可以选择不执行流水线(顺序或并行)。在这种情况下,将不遍历任何源元素,也不会评估任何中间操作。与副作用有关的行为参数会受到影响,但强烈不建议使用除调试之外的有副作用的行为参数。

List<String> l = Arrays.asList("A", "B", "C", "D");
long count = l.stream().peek(System.out::println).count();

流源(List)覆盖的元素数目已知,中间操作 peek 不会向流中注入或删除元素(如 flatMap 或 filter 操作可能的情况)。因此计数是列表的大小,无需执行管道并打印出列表元素作为副作用。

这些引用仅出现在 Java 9 的 Javadoc 中,因此它必须是一种新的优化。


9
值得注意的是,Java 8中 count()方法的Javadoc与此不同。 - eis
7
Stream.peek的文档中可以得知:在一些情况下,流的实现能够优化掉某些或全部元素的生成(例如使用类似于findFirst的短路操作,或在“count”示例中所述的情况),对于那些元素,该操作将不会被调用。 - Naman
4
@nullpointer这也仅在Java 9文档中提到,尽管它一直是含蓄的。 - Holger

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