您可以使用递归
flatMap链解决此问题。
首先,当我们需要在映射值之间来回移动时,最好将它们复制到
ArrayList(这不是深度复制,在您的情况下,它
ArrayList仅包含3个元素,因此额外的内存使用率很低)。
其次,为维护先前访问的元素的前缀,让我们创建一个助手不可变
Prefix类:
private static class Prefix<T> { final T value; final Prefix<T> parent; Prefix(Prefix<T> parent, T value) { this.parent = parent; this.value = value; } // put the whole prefix into given collection <C extends Collection<T>> C addTo(C collection) { if (parent != null) parent.addTo(collection); collection.add(value); return collection; }}这是一个非常简单的不可变链表,可以像这样使用:
List<String> list = new Prefix<>(new Prefix<>(new Prefix<>(null, "a"), "b"), "c") .addTo(new ArrayList<>()); // [a, b, c];
接下来,让我们创建链接flatMaps的内部方法:
private static <T, C extends Collection<T>> Stream<C> comb( List<? extends Collection<T>> values, int offset, Prefix<T> prefix, Supplier<C> 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 <T, C extends Collection<T>> Stream<C> ofCombinations( Collection<? extends Collection<T>> values, Supplier<C> supplier) { if (values.isEmpty()) return Stream.empty(); return comb(new ArrayList<>(values), 0, null, supplier);}一个用法示例:
Map<String, Collection<String>> map = new linkedHashMap<>(); // to preserve the ordermap.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)。



