如何使用Java8流生成笛卡尔积?

如何使用Java8流生成笛卡尔积?,java,java-8,java-stream,cartesian-product,Java,Java 8,Java Stream,Cartesian Product,我有以下收集类型: Map<String, Collection<String>> map; 我希望得到的结果将是一个列表结果,看起来类似于(排序并不重要,它只需要是一个包含所有可能组合的“完整”结果): 这基本上是一个计数问题,但我想看看是否可以使用Java8流解决此问题。您可以使用递归flatMap链解决此问题 首先,由于我们需要根据映射值来回移动,因此最好将它们复制到ArrayList(这不是深度复制,在您的示例中,它只是3个元素的ArrayList,因此额外的内

我有以下收集类型:

Map<String, Collection<String>> map;
我希望得到的结果将是一个
列表
结果,看起来类似于(排序并不重要,它只需要是一个包含所有可能组合的“完整”结果):


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

您可以使用递归
flatMap
链解决此问题

首先,由于我们需要根据映射值来回移动,因此最好将它们复制到
ArrayList
(这不是深度复制,在您的示例中,它只是3个元素的
ArrayList
,因此额外的内存使用率较低)

其次,为了维护以前访问过的元素的前缀,让我们创建一个helper immutable
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;
    }
}
用法示例:

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);
Map Map=newlinkedhashmap();//维持秩序
map.put(“A”,数组.asList(“a1”、“a2”、“a3”、“a4”);
map.put(“B”,Arrays.asList(“b1”,“b2”,“b3”);
map.put(“C”,Arrays.asList(“c1”,“c2”);
of组合(map.values(),LinkedHashSet::new).forEach(System.out::println);

我们再次将各个组合收集到
LinkedHashSet
以保持顺序。您可以使用任何其他集合(例如,
ArrayList::new
)。

您可以使用递归
flatMap
链解决此问题

首先,由于我们需要根据映射值来回移动,因此最好将它们复制到
ArrayList
(这不是深度复制,在您的示例中,它只是3个元素的
ArrayList
,因此额外的内存使用率较低)

其次,为了维护以前访问过的元素的前缀,让我们创建一个helper immutable
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;
    }
}
用法示例:

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);
Map Map=newlinkedhashmap();//维持秩序
map.put(“A”,数组.asList(“a1”、“a2”、“a3”、“a4”);
map.put(“B”,Arrays.asList(“b1”,“b2”,“b3”);
map.put(“C”,Arrays.asList(“c1”,“c2”);
of组合(map.values(),LinkedHashSet::new).forEach(System.out::println);

我们再次将各个组合收集到
LinkedHashSet
以保持顺序。您可以使用任何其他集合(例如,
ArrayList::new
)。

这里是另一个解决方案,它不像Tagir的示例那样使用来自
流的许多功能;不过,我认为这更直截了当:

公共类置换{
临时列表perms;
公共列表(地图){
SortedMap SortedMap=新树映射();
分类地图(地图);
sortedMap.values().forEach((v)->perms=expand(perms,v));
回烫;
}
私有列表扩展(
列表、集合元素){
List newList=newlinkedlist();
if(list==null){
元素。forEach((e)->{
SortedSet集合=新树集合();
增加(e);
newList.add(set);
});
}否则{
list.forEach((集合)->
元素。forEach((e)->{
SortedSet newSet=新树集();
newSet.addAll(集合);
新闻集。添加(e);
添加(新闻集);
}));
}
返回newList;
}
}
如果您对元素的排序不感兴趣,可以删除
排序的
前缀;尽管如此,我认为如果一切都已排序,那么调试就更容易了

用法:

Permutations p=新排列();
List plist=p.List(地图);
plist.forEach((s)->System.out.println(s));

享受吧

这里是另一个解决方案,它不像Tagir的例子那样使用
中的许多功能;不过,我认为这更直截了当:

公共类置换{
临时列表perms;
公共列表(地图){
SortedMap SortedMap=新树映射();
分类地图(地图);
sortedMap.values().forEach((v)->perms=expand(perms,v));
回烫;
}
私有列表扩展(
列表、集合元素){
List newList=newlinkedlist();
if(list==null){
元素。forEach((e)->{
SortedSet集合=新树集合();
增加(e);
newList.add(set);
});
}否则{
list.forEach((集合)->
元素。forEach((e)->{
SortedSet newSet=新树集();
newSet.addAll(集合);
新闻集。添加(e);
添加(新闻集);
}));
}
返回newList;
}
}
如果您对元素的排序不感兴趣,可以删除
排序的
前缀;尽管如此,我认为如果一切都已排序,那么调试就更容易了

用法:

Permutations p=新排列();
List plist=p.List(地图);
plist.forEach((s)->System.out.println(s));

享受吧

一个主要在列表上操作的解决方案,使事情变得更简单。它在
flatMap
中执行递归调用,跟踪已经组合的元素和仍然缺少的元素集合,并以列表流的形式提供此嵌套递归构造的结果:

import java.util.*;
导入java.util.stream.stream;
公共类卡特尔产品{
公共静态void main(字符串[]args){
地图=
新建LinkedHashMap();
map.put(“A”,数组.asList(“a1”、“a2”、“a3”、“a4”);
map.put(“B”,Arrays.asList(“b1”,“b2”,“b3”);
map.put(“C”,Arrays.asList(“c1”,“c2”);
of组合(map.values()).forEach(System.out::println);
}
公共静态组合流(

Collection主要对列表进行操作的解决方案,使
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));
}
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);
List<String> listA = new ArrayList<>();
listA.add("0");
listA.add("1");
List<String> listB = new ArrayList<>();
listB.add("a");
listB.add("b"); 

List<String> cartesianProduct = new ArrayList<>();
listA.forEach(a -> listB.forEach(b -> cartesianProduct.add(a + b)));

cartesianProduct.forEach(System.out::println);
//Output : 0a 0b 1a 1b 
class CartesianProduct<T> implements Iterable<List<T>> {
    private final Iterable<? extends Iterable<T>> factors;

    public CartesianProduct(final Iterable<? extends Iterable<T>> factors) {
        this.factors = factors;
    }

    @Override
    public Iterator<List<T>> iterator() {
        return new CartesianProductIterator<>(factors);
    }
}

class CartesianProductIterator<T> implements Iterator<List<T>> {
    private final List<Iterable<T>> factors;
    private final Stack<Iterator<T>> iterators;
    private final Stack<T> current;
    private List<T> next;
    private int index = 0;

    private void computeNext() {
        while (true) {
            if (iterators.get(index).hasNext()) {
                current.add(iterators.get(index).next());
                if (index == factors.size() - 1) {
                    next = new ArrayList<>(current);
                    current.pop();
                    return;
                }
                index++;
                iterators.add(factors.get(index).iterator());
            } else {
                index--;
                if (index < 0) {
                    return;
                }
                iterators.pop();
                current.pop();
            }
        }
    }

    public CartesianProductIterator(final Iterable<? extends Iterable<T>> factors) {
        this.factors = StreamSupport.stream(factors.spliterator(), false)
                .collect(Collectors.toList());
        if (this.factors.size() == 0) {
            index = -1;
        }
        iterators = new Stack<>();
        iterators.add(this.factors.get(0).iterator());
        current = new Stack<>();
        computeNext();
    }

    @Override
    public boolean hasNext() {
        if (next == null && index >= 0) {
            computeNext();
        }
        return next != null;
    }

    @Override
    public List<T> next() {
        if (!hasNext()) {
            throw new IllegalStateException();
        }
        var result = next;
        next = null;
        return result;
    }
}
Set<List<String>> result = Sets.cartesianProduct(Set.of("a1","a2"), Set.of("b1","b2"), Set.of("c1","c2" ))
[a1, b1, c1]  [a2, b1, c1]  [a3, b1, c1]
[a1, b1, c2]  [a2, b1, c2]  [a3, b1, c2]
[a1, b1, c3]  [a2, b1, c3]  [a3, b1, c3]
[a1, b2, c1]  [a2, b2, c1]  [a3, b2, c1]
[a1, b2, c2]  [a2, b2, c2]  [a3, b2, c2]
[a1, b2, c3]  [a2, b2, c3]  [a3, b2, c3]
[a1, b3, c1]  [a2, b3, c1]  [a3, b3, c1]
[a1, b3, c2]  [a2, b3, c2]  [a3, b3, c2]
[a1, b3, c3]  [a2, b3, c3]  [a3, b3, c3]