比Stream.peek()

时间:2017-02-08 15:39:52

标签: java lambda java-8

“peek”主要用于调试。如果我想在流的中间调用流上的方法,这会改变流对象的状态。

Stream.of("Karl", "Jill", "Jack").map(Test::new).peek(t->t.setLastName("Doe"));

我能做到:

Stream.of("Karl", "Jill", "Jack").map(Test::new).map(t->{t.setLastName("Doe"); return t;});

但这看起来很难看。这是不应该做的事情还是有更好的方法来做到这一点?

编辑:forEach除了它是终端操作之外还有效,因此您之后无法继续处理该流。然后我会期望制作一个Collection,do forEach,然后再开始流式传输Collection。

编辑:map(Class::processingMethod)是我现在正在做的事情,但由于processingMethod只是返回this,所以它似乎是对地图的误用。另外,它实际上并不像商业逻辑那样。

最终编辑:我接受了@Holger的回答。不能期望Stream.peek处理Stream上的所有元素,因为它不是终端操作。 map也是如此。即使您可能已经使用可以保证它将处理所有操作的内容终止了您的流,但您不应该编写希望每个用户都这样做的代码。因此,要进行处理,您应该在forEach上使用Collection,然后根据需要再次开始流式传输Collection

3 个答案:

答案 0 :(得分:9)

您过度使用方法引用。 Test::new的简单性不值得,如果它使您的其他流使用变得复杂。

一个明确的解决方案是:

Stream.of("Karl", "Jill", "Jack")
      .map(first -> { Test t = new Test(first); t.setLastName("Doe"); return t; })
      …

或更好

Stream.of("Karl", "Jill", "Jack").map(first -> new Test(first, "Doe")) …

假设该类具有不那么遥不可及的构造函数,同时接受这两个名称。

上面的代码解决了用例,其中操作操纵本地构造的对象,因此仅当对象将被后续Stream操作使用时,操作才是相关的。对于其他情况,如果操作对Stream外部的对象产生副作用,则滥用map几乎解决了In Java streams, is peek really only for debugging?中所解释的peek的所有缺点

答案 1 :(得分:6)

即使您使用两个参数创建另一个构造函数,也无法使用方法引用。

唯一的方法是:

.map(token -> {Test t = new Test(token); token.setLastname("joe"); return t;})

答案 2 :(得分:0)

并非在每种情况下,为您要实现的目标定义新方法或构造函数都是有意义的。您可以为具有以下功能的函数创建工具类Functions

public static <T, R> Function<T,R> of(Function<T, R> function){
    return function;
}


public static <T> Function<T,T> peek(Consumer<? super T> peeker){
    return t -> {
        peeker.accept(t);
        return t;
    };
}

然后你可以像这样使用它们:

Stream.of("Karl", "Jill", "Jack").map(Functions.of(Test::new).andThen(Functions.peek(t -> t.setLastName("Doe"))));

不应该通过映射(应该是副作用免费)轻松地为每个元素设置姓氏来打扰流。因此,我在此示例中直接将其编写为构造函数。