小编典典
您可以使用递归flatMap链解决此问题。
首先,当我们需要在映射值之间来回移动时,最好将它们复制到ArrayList(这不是深度复制,在您的情况下,它ArrayList仅包含3个元素,因此额外的内存使用率很低)。
其次,为维护先前访问的元素的前缀,让我们创建一个助手不可变Prefix类:
private static class Prefix {
final T value;
final Prefix parent;
Prefix(Prefix parent, T value) {
this.parent = parent;
this.value = value;
}
// put the whole prefix into given collection
> C addTo(C collection) {
if (parent != null)
parent.addTo(collection);
collection.add(value);
return collection;
}
}
这是一个非常简单的不可变链表,可以像这样使用:
List list = new Prefix<>(new Prefix<>(new Prefix<>(null, "a"), "b"), "c")
.addTo(new ArrayList<>()); // [a, b, c];
接下来,让我们创建链接flatMaps的内部方法:
private static > Stream comb(
List extends Collection> values, int offset, Prefix prefix,
Supplier supplier) {
if (offset == values.size() - 1)
return values.get(offset).stream()
.map(e -> new Prefix<>(prefix, e).addTo(supplier.get()));
return values.get(offset).stream()
.flatMap(e -> comb(values, offset + 1, new Prefix<>(prefix, e), supplier));
}
看起来像递归,但更复杂:它不会直接调用自身,而是传递了lambda来调用外部方法。参数:
values:List原始值(new ArrayList<>(map.values)根据您的情况)。
offset:此列表中的当前偏移量
前缀:长度的偏移的当前前缀(或null如果offset == 0)。它包含当前从集合中选择的元素list.get(0),list.get(1)直到list.get(offset-1)。
供应商:创建结果集合的工厂方法。
到达值列表(offset == values.size() -
1)的末尾时,我们使用供应商将最后一个集合的元素从值映射到最终组合。否则,我们flatMap对每个中间元素使用which来放大前缀,并comb为下一个偏移量再次调用该方法。
最后是使用此功能的公共方法:
public static > Stream ofCombinations(
Collection extends Collection> values, Supplier supplier) {
if (values.isEmpty())
return Stream.empty();
return comb(new ArrayList<>(values), 0, null, supplier);
}
一个用法示例:
Map> map = new LinkedHashMap<>(); // to preserve the order
map.put("A", Arrays.asList("a1", "a2", "a3", "a4"));
map.put("B", Arrays.asList("b1", "b2", "b3"));
map.put("C", Arrays.asList("c1", "c2"));
ofCombinations(map.values(), LinkedHashSet::new).forEach(System.out::println);
我们LinkedHashSet再次收集单个组合以保留订单。您可以改用其他任何集合(例如ArrayList::new)。
2020-09-11