与Java 8流的生成器模式

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

WebTarget target = getClient().target(u); for (Entry queryParam : queryParams.entrySet()) { target = target.queryParam(queryParam.getKey(), queryParam.getValue()); } 

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

遗憾的是,流API中没有foldLeft方法。 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]; 

我坚持原来的答案,并声称在这种情况下,你的老式for循环是一种更好的方法。

为Java 8流实现正确的foldLeft并不是很困难:

 @SuppressWarnings("unchecked") public static  U foldLeft(Stream stream, U identity, BiFunction accumulator) { Object[] result = new Object[] { identity }; stream.forEachOrdered(t -> result[0] = accumulator.apply((U) result[0], t)); return (U) result[0]; } 

或者以类型安全的方式:

 public static  U foldLeft(Stream stream, U identity, BiFunction 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 ,您甚至可以使用并行流获得速度增益:在这种情况下,下一个元素可以通过mapfoldLeft处理的当前元素并行处理。 我不同意这种操作不适合Stream API,因为它可以通过已经存在的forEachOrdered正确表达。

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

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