gpt4 book ai didi

java - 如何使用 Java 8 流制作笛卡尔积?

转载 作者:太空宇宙 更新时间:2023-11-04 12:54:44 26 4
gpt4 key购买 nike

我有以下集合类型:

Map<String, Collection<String>> map;

我想为map.size()中的每一个创建独特的组合来自每个键的集合中的单个值。

例如,假设 map 如下所示:

A, {a1, a2, a3, ..., an}
B, {b1, b2, b3, ..., bn}
C, {c1, c2, c3, ..., cn}

我想要得到的结果是 List<Set<String>>结果,看起来类似于(顺序并不重要,它只需要是由所有可能的组合组成的“完整”结果):

{a1, b1, c1},
{a1, b1, c2},
{a1, b1, c3},
{a1, b2, c1},
{a1, b2, c2},
{a1, b2, c3},
...
{a2, b1, c1},
{a2, b1, c2},
...
{a3, b1, c1},
{a3, b1, c2},
...
{an, bn, cn}

这基本上是一个计数问题,但我想看看是否可以使用 Java 8 流解决方案。

最佳答案

您可以使用递归 flatMap 来解决这个问题链。

首先,由于我们需要通过 map 值来回移动,因此最好将它们复制到 ArrayList (这不是深层复制,在您的情况下,它只是 3 个元素的 ArrayList,因此额外的内存使用量很低)。

其次,为了维护先前访问过的元素的前缀,让我们创建一个不可变的帮助器 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 调用外部方法。参数:

  • 值:List原始值( new ArrayList<>(map.values) 在您的情况下)。
  • offset:此列表中的当前偏移量
  • prefix:长度偏移量的当前前缀(或 null if offset == 0 )。它包含当前从集合 list.get(0) 中选择的元素, list.get(1)高达list.get(offset-1) .
  • supplier:创建结果集合的工厂方法。

当我们到达值列表的末尾( offset == values.size() - 1 )时,我们使用供应商将最后一个集合的元素从值映射到最终组合。否则我们使用flatMap对于每个中间元素,放大前缀并调用 comb再次调用下一个偏移量的方法。

最后是使用此功能的公共(public)方法:

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 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 )。

关于java - 如何使用 Java 8 流制作笛卡尔积?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35506963/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com