我在JavaScript中有一个笛卡尔积函数:
function cartesianProduct(arr) {
return arr.reduce(function(a,b) {
return a.map(function(x) {
return b.map(function(y) {
return x.concat(y);
});
}).reduce(function(a,b) { return a.concat(b); }, []);
}, [[]]);
}
所以,如果我有一个3D数组:
var data = [[['D']], [['E'],['L','M','N']]];
cartesianProduct(data)
的结果将是2D数组:
[['D','E'], ['D','L','M','N']]
我要做的是使用Streams在Java中编写这个笛卡尔积函数。
到目前为止,我在Java中有以下内容:
public Collection<Collection<String>> cartesianProduct(Collection<Collection<Collection<String>>> arr) {
return arr.stream().reduce(new ArrayList<Collection<String>>(), (a, b) -> {
return a.stream().map(x -> {
return b.stream().map(y -> {
return Stream.concat(x.stream(), y.stream());
});
}).reduce(new ArrayList<String>(), (c, d) -> {
return Stream.concat(c, d);
});
});
}
我有一个类型检查错误,指出:
不兼容
ArrayList<String>
与Stream<Stream<String>>
我猜错了什么:
Stream.concat
之后)答案 0 :(得分:2)
这可以通过一些函数式编程魔术实现。这是接受Collection<Collection<Collection<T>>>
并生成Stream<Collection<T>>
的方法:
static <T> Stream<Collection<T>> cartesianProduct(Collection<Collection<Collection<T>>> arr)
{
return arr.stream()
.<Supplier<Stream<Collection<T>>>> map(c -> c::stream)
.reduce((s1, s2) -> () -> s1.get().flatMap(
a -> s2.get().map(b -> Stream.concat(a.stream(), b.stream())
.collect(Collectors.toList()))))
.orElseGet(() -> () -> Stream.<Collection<T>>of(Collections.emptyList()))
.get();
}
用法示例:
cartesianProduct(
Arrays.asList(Arrays.asList(Arrays.asList("D")),
Arrays.asList(Arrays.asList("E"), Arrays.asList("L", "M", "N"))))
.forEach(System.out::println);
输出:
[D, E]
[D, L, M, N]
当然,如果您想要返回.forEach()
而不是List
,则可以将结果收集到Collection<Collection<T>>
,但是返回Stream
对我来说似乎更灵活。< / p>
一点解释:
在这里,我们通过map(c -> c::stream)
创建了一个流供应商流。该流的每个功能可以按需生成相应集合元素的流。我们这样做是因为一次性流(否则有流的流就足够了)。之后,我们减少这个供应商流,为每一对创建一个新供应商,flatMaps两个流并将其元素映射到连接列表。 orElseGet
部分是处理空输入所必需的。最后一个.get()
只调用最终的流供应商来获取结果流。