具有 Java 8 Stream 的构建器模式

Builder pattern with a Java 8 Stream

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

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

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

我想使用 Java8 流 API 做同样的事情,但我不知道该怎么做。让我挣扎的是目标每次都被重新分配,所以一个简单的 .forEach() 是行不通的。我想我需要使用 .collect() 或 reduce() 因为我正在寻找单个 return 值但我现在迷路了!

很遗憾,流 API 中没有 foldLeft 方法。原因由Stuart Marks in this answer解释:

[...] Finally, Java doesn't provide foldLeft and foldRight operations because they imply a particular ordering of operations that is inherently sequential. This clashes with the design principle stated above of providing APIs that support sequential and parallel operation equally.

最终你在这里尝试做的是一些程序性/顺序性的事情,所以我认为流 API 不适合这个用例。我认为您自己发布的 for-each 循环已经很好了。

更新:

正如@TagirValeev 指出的那样 you can in fact solve it with the stream API (using forEachOrdered。你的代码看起来像

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

虽然我坚持我原来的答案,并声称在这种情况下你的旧 for-loop 是更好的方法。

为 Java 8 个流实施正确的 foldLeft 并不难:

@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];
}

或者以类型安全的方式:

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;
}

这适用于顺序流和并行流。如果您的流有一些 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()))