具有 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()))
我正在用一个简单的循环构建一个对象:
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
andfoldRight
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 指出的那样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()))