与Java 8流的生成器模式

Fra*_*sco 9 java builder java-8 java-stream

我正在使用一个简单的循环构建一个对象:

WebTarget target = getClient().target(u);

for (Entry<String, String> queryParam : queryParams.entrySet()) {
    target = target.queryParam(queryParam.getKey(), queryParam.getValue());
}
Run Code Online (Sandbox Code Playgroud)

我想使用Java8 Stream API做同样的事情,但我无法弄清楚如何做到这一点.让我挣扎的是每次都重新分配目标,所以简单的.forEach()将不起作用.我想我需要使用.collect()或reduce(),因为我正在寻找单个返回值,但此刻我迷路了!

Tag*_*eev 11

实现foldLeftJava 8流的正确性并不是很困难:

@SuppressWarnings("unchecked")
public static <T, U> U foldLeft(Stream<T> stream, 
                                U identity, BiFunction<U, ? super T, U> accumulator) {
    Object[] result = new Object[] { identity };
    stream.forEachOrdered(t -> result[0] = accumulator.apply((U) result[0], t));
    return (U) result[0];
}
Run Code Online (Sandbox Code Playgroud)

或者以类型安全的方式:

public static <T, U> U foldLeft(Stream<T> stream, 
                                U identity, BiFunction<U, ? super T, U> accumulator) {
    class Box {
        U value;
        Box(U value) { this.value = value; }
    }
    Box result = new Box(identity);
    stream.forEachOrdered(t -> result.value = accumulator.apply(result.value, t));
    return result.value;
}
Run Code Online (Sandbox Code Playgroud)

这适用于顺序和并行流.如果您的流具有一些消耗CPU的无状态中间操作,您甚至可以使用并行流来获得速度增益map:在这种情况下,下一个元素可以与处理map的当前元素并行处理foldLeft.我不同意这种操作不适合Stream API,因为它可以通过已经存在的方式正确表达forEachOrdered.

我在我的StreamEx库中有这个操作,所以你可以像这样使用它:

WebTarget target = EntryStream.of(queryParams).foldLeft(getClient().target(u), 
        (t, entry) -> t.queryParam(entry.getKey(), entry.getValue()))
Run Code Online (Sandbox Code Playgroud)

  • 如果你想要类型安全,可以使用AtomicReference <U>,虽然我不确定它是否会产生最终的lambda惯用语:AtomicReference <U> result = new AtomicReference <>(identity); stream.forEachOrdered(t - > result.updateAndGet(u - > accumulator.apply(u,t)); return result.get(); (3认同)

aio*_*obe 6

遗憾的foldLeft是,流API中没有方法.Stuart Marks这个答案中解释了这个原因:

[...]最后,Java不提供foldLeftfoldRight操作,因为它们意味着特定的操作顺序本质上是顺序的.这与上述设计原则相冲突,即提供支持顺序和并行操作的API.

最终你在这里尝试做的是程序/顺序,所以我不认为流API非常适合这个用例.我认为你自己发布的for-each循环就像它得到的一样好.

更新:

作为@TagirValeev指出下面可以实际上与流API解决这个问题(使用forEachOrdered.然后,您的代码会看起来像

WebTarget[] arr = { getClient().target(u) };
queryParams.entrySet()
           .stream()
           .forEachOrdered(e -> arr[0] = arr[0].queryParam(e.getKey(),
                                                           e.getValue()));
WebTarget target = arr[0];
Run Code Online (Sandbox Code Playgroud)

我支持我的原始答案,并声称在这种情况下,你的旧的for-loop是一种更好的方法.

  • 我想我更喜欢一个直截了当地表示流不能真正支持这个问题的答案. (2认同)