我想创建这产生其是多个给定的流(由二进制运算符聚集以在端部是同一类型)的笛卡尔积元件的流的方法。 请注意,这两个参数和结果是流,而不是收藏。
例如,对于两个流{A,B}和{X,Y}我想它产生值{AX,AY,BX,BY}(简单级联用于聚集串)的流。 到目前为止,我已经想出了这样的代码:
private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator, Stream<T>... streams) {
Stream<T> result = null;
for (Stream<T> stream : streams) {
if (result == null) {
result = stream;
} else {
result = result.flatMap(m -> stream.map(n -> aggregator.apply(m, n)));
}
}
return result;
}
这是我期望的使用情况:
Stream<String> result = cartesian(
(a, b) -> a + b,
Stream.of("A", "B"),
Stream.of("X", "Y")
);
System.out.println(result.collect(Collectors.toList()));
预期结果: AX, AY, BX, BY
。
另一个例子:
Stream<String> result = cartesian(
(a, b) -> a + b,
Stream.of("A", "B"),
Stream.of("K", "L"),
Stream.of("X", "Y")
);
预期结果: AKX, AKY, ALX, ALY, BKX, BKY, BLX, BLY
。
但是,如果我运行代码,我得到这个错误:
IllegalStateException异常:流已在运行或关闭
哪里流消耗? 通过flatMap? 是否可以很容易解决吗?
在你的榜样传递的流永远不会比通过列表更好:
private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator, List<T>... lists) {
...
}
并使用它像这样:
Stream<String> result = cartesian(
(a, b) -> a + b,
Arrays.asList("A", "B"),
Arrays.asList("K", "L"),
Arrays.asList("X", "Y")
);
在这两种情况下,你创建从可变参数隐式阵列,并用它作为数据源,因此,懒惰是虚。 您的数据实际存储在阵列中。
在大多数情况下所产生的笛卡儿积流比投入要长得多,因此几乎没有理由使输入懒惰。 例如,具有五行(总共25)的五个列表,你将不得不将所得3125个元素流。 因此,存储25种元素的内存不是很大的问题。 其实在大多数的实际情况,他们已经存储在内存中。
为了产生笛卡尔积流,你需要不断的“倒带”的所有流(除了第一个)。 要快退,流应该能够一次又一次地找回原来的数据,无论是缓冲它们以某种方式(你不喜欢的),或从源代码重新抓住他们(colleciton,阵列,文件,网络,随机数,等等。 ),并一次又一次地执行所有的中间业务。 如果您的源和中间业务是缓慢的,那么懒惰的解决方案可能比的缓冲液,慢得多。 如果源是无法再次产生的数据(例如,随机数发生器,它不能产生之前生产的相同号码),您的解决方案将是不正确的。
尽管如此完全懒惰的解决方案是possbile。 只要使用不流,但流供应商:
private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator,
Supplier<Stream<T>>... streams) {
return Arrays.stream(streams)
.reduce((s1, s2) ->
() -> s1.get().flatMap(t1 -> s2.get().map(t2 -> aggregator.apply(t1, t2))))
.orElse(Stream::empty).get();
}
为我们营造和减少供应商的流来获得所产生的供应商,并最终把它的解决方案是有趣的。 用法:
Stream<String> result = cartesian(
(a, b) -> a + b,
() -> Stream.of("A", "B"),
() -> Stream.of("K", "L"),
() -> Stream.of("X", "Y")
);
result.forEach(System.out::println);
stream
在消耗flatMap
在第二次迭代操作。 所以,你必须创建一个新的流你每次map
你的结果。 因此,你必须收集stream
提前获得在每个迭代一个新的流。
private static <T> Stream<T> cartesian(BiFunction<T, T, T> aggregator, Stream<T>... streams) {
Stream<T> result = null;
for (Stream<T> stream : streams) {
if (result == null) {
result = stream;
} else {
Collection<T> s = stream.collect(Collectors.toList());
result = result.flatMap(m -> s.stream().map(n -> aggregator.apply(m, n)));
}
}
return result;
}
或者更短:
private static <T> Stream<T> cartesian(BiFunction<T, T, T> aggregator, Stream<T>... streams) {
return Arrays.stream(streams).reduce((r, s) -> {
List<T> collect = s.collect(Collectors.toList());
return r.flatMap(m -> collect.stream().map(n -> aggregator.apply(m, n)));
}).orElse(Stream.empty());
}