当前位置: 首页 > 知识库问答 >
问题:

通过JavaStream实现多个集合的笛卡尔产品

颜森
2023-03-14

现在我只能实现两个集合的笛卡尔积,下面是代码:

public static <T1, T2, R extends Collection<Pair<T1, T2>>>
R getCartesianProduct(
        Collection<T1> c1, Collection<T2> c2,
        Collector<Pair<T1, T2>, ?, R> collector) {
    return c1.stream()
            .flatMap(e1 -> c2.stream().map(e2 -> new Pair<>(e1, e2)))
            .collect(collector);
}

这段代码在IntelliJ中运行良好,但在Eclipse中不起作用。编译器符合性级别均为1.8:

The method collect(Collector<? super Object,A,R>)
in the type Stream<Object> is not applicable for
the arguments (Collector<Pair<T1,T2>,capture#5-of ?,R>)

这里是Pair.java:

public class Pair<T1, T2> implements Serializable {
    protected T1 first;
    protected T2 second;
    private static final long serialVersionUID = 1360822168806852921L;

    public Pair(T1 first, T2 second) {
        this.first = first;
        this.second = second;
    }

    public Pair(Pair<T1, T2> pair) {
        this(pair.getFirst(), pair.getSecond());
    }

    public T1 getFirst() {
        return this.first;
    }

    public T2 getSecond() {
        return this.second;
    }

    public void setFirst(T1 o) {
        this.first = o;
    }

    public void setSecond(T2 o) {
        this.second = o;
    }

    public String toString() {
        return "(" + this.first + ", " + this.second + ")";
    }

    @Override
    public boolean equals(Object o) {
        if(!(o instanceof Pair))
            return false;
        Pair p = (Pair) o;
        if(!this.first.equals(p.first))
            return false;
        if(!this.second.equals(p.second))
            return false;
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 1;
        hash = hash * 31 + this.first.hashCode();
        hash = hash * 31 + this.second.hashCode();
        return hash;
    }
}

如何修复此错误?

有没有一种优雅的方法来实现几个集合的笛卡尔产品?假设我们有类tuple

共有3个答案

汪阳飇
2023-03-14

您可以使用Supplier作为收集器的参数。toCollection方法。这简化了代码并使其更通用。不同类型和数量的集合及其元素的笛卡尔积。

在线试试吧!

public static void main(String[] args) {
    Set<Integer> a = Set.of(1, 2);
    Set<Character> b = Set.of('*', '+');
    List<String> c = List.of("A", "B");

    Set<Set<Object>> cpSet = cartesianProduct(HashSet::new, a, b, c);
    List<List<Object>> cpList = cartesianProduct(ArrayList::new, a, b, c);

    // output, order may vary
    System.out.println(cpSet);
    System.out.println(cpList);
}
java prettyprint-override">/**
 * @param nCol the supplier of the output collection
 * @param cols the input array of collections
 * @param <R>  the type of the return collection
 * @return the cartesian product of the multiple collections
 */
@SuppressWarnings("unchecked")
public static <R extends Collection<?>> R cartesianProduct(
        Supplier nCol, Collection<?>... cols) {
    // check if supplier is not null
    if (nCol == null) return null;
    return (R) Arrays.stream(cols)
        // non-null and non-empty collections
        .filter(col -> col != null && col.size() > 0)
        // represent each element of a collection as a singleton collection
        .map(col -> (Collection<Collection<?>>) col.stream()
            .map(e -> Stream.of(e).collect(Collectors.toCollection(nCol)))
            .collect(Collectors.toCollection(nCol)))
        // summation of pairs of inner collections
        .reduce((col1, col2) -> (Collection<Collection<?>>) col1.stream()
            // combinations of inner collections
            .flatMap(inner1 -> col2.stream()
                // concatenate into a single collection
                .map(inner2 -> Stream.of(inner1, inner2)
                    .flatMap(Collection::stream)
                    .collect(Collectors.toCollection(nCol))))
            // collection of combinations
            .collect(Collectors.toCollection(nCol)))
        // otherwise an empty collection
        .orElse((Collection<Collection<?>>) nCol.get());
}

输出(顺序可能有所不同):

[[1,A,*],[1,B,*],[1,A,+],[A,2,*],[1,B,+],[2,B,*],[A,2,+],[2,B,+]]
[[2,+,A],[2,+,B],[2,*,A],[2,*,B],[1,+,A],[1,+,B],[1,*,A],[1,*,B]]

另请参见不太通用的版本:在Java中查找笛卡尔积

谷梁德容
2023-03-14

这里有一个解决方案,它概括了编译时不知道必需的平面地图应用程序的数量(即产品的顺序)的情况。

BinaryOperator<Function<String,Stream<String>>> kleisli = (f,g) -> s -> f.apply(s).flatMap(g);

List<String> cartesian(int n, Collection<String> coll) {
    return coll.stream()
           .flatMap( IntStream.range(1, n).boxed()
                     .map(_any -> crossWith(coll::stream, String::concat)) // create (n-1) appropriate crossWith instances
                     .reduce(s -> Stream.of(s), kleisli)                   // compose them sequentially
                    )                                                      // flatMap the stream with the entire function chain
           .collect(toList());
}

你会在我自己的博客上找到这是如何工作的细节。

慕晨
2023-03-14

Eclipse在类型推断方面存在问题。如果添加类型提示

如果我可以建议一个不同的方法,考虑让你的cartesianProducts不做整个流和集合,而只是一个帮手的平面地图

static <T1, T2, R> Function<T1, Stream<R>> crossWith(
         Supplier<? extends Stream<T2>> otherSup, 
         BiFunction<? super T1, ? super T2, ? extends R> combiner
) {
    return t1 -> otherSup.get().map(t2 -> combiner.apply(t1, t2));
}

现在,如果希望结果包含Pairs,则只需创建Pair,并且可以通过多次应用flatMap来进行高阶笛卡尔积:

List<String> letters = Arrays.asList("A", "B", "C");
List<Integer> numbers = Arrays.asList(1, 2, 3);

List<Pair<String, Integer>> board = letters.stream()
                .flatMap(crossWith(numbers::stream, Pair::new))
                .collect(toList());


List<String> ops = Arrays.asList("+", "-", "*", "/");

List<String> combinations = letters.stream()
                .flatMap(crossWith(ops::stream, String::concat))
                .flatMap(crossWith(letters::stream, String::concat))
                .collect(toList());   // triple cartesian product

 类似资料:
  • 现在我只能实现两个集合的笛卡尔积,下面是代码: 这段代码在IntelliJ中运行良好,但在Eclipse(两者的编译器遵从级别均为1.8)中就不行了: 下面是pair.java: 如何修复这个错误? 有没有一个优雅的方法来实现几个收藏的笛卡尔产品?(假设我们有类)

  • 本文向大家介绍map reduce实现笛卡尔乘积?相关面试题,主要包含被问及map reduce实现笛卡尔乘积?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: 在Map阶段,将来自矩阵A的元素标识成l条<key,value>的形式,key=(i,k),k=1,2,…,l。value=(j,)。将来自矩阵B的元素标识成l条<key,value>的形式,key=(i,k),k=1,2,…,m。

  • 问题内容: 您将如何在JavaScript中实现多个数组的笛卡尔积? 举个例子, 应该回来 问题答案: 这是使用和提供的解决问题的功能解决方案(没有任何 可变变量 !),该提供者为:

  • 问题内容: 定义:两个集合的笛卡尔积是这些集合的所有可能对的集合,因此{A,B} x {a,b} = {(A,a),(A,b),(B,a ),(B,b)}。 现在,我想将这样的笛卡尔积插入数据库表(每对成一行)。打算在表中使用每对的默认值,因此此时数据库中不存在数据(即两组)。 任何想法如何使用postgresql实现这一目标? 编辑 : 借助Grzegorz Szpetkowski的答案,我能够

  • 问题内容: 你是否知道一些精巧的Java库,可让你制作两个(或更多)集合的笛卡尔积? 例如:我有三套。一个对象是Person类的对象,第二个对象是Gift的对象,第三个对象是GiftExtension的对象。 我想生成一个包含所有可能的三元组的集合。 集的数量可能会有所不同,因此我无法在嵌套的foreach循环中执行此操作。在某些情况下,我的应用程序需要制作Person-Gift对的乘积,有时是的

  • 问题内容: 我可以通过以下方法轻松地在Scala中实现此目标: 因此,如果我给它{1,2},{3,4},我将返回{1,3},{1,4},{2,3},{2,4} 我希望能够使用流将其转换为java 8。 我有点困难,我希望能够进一步扩展,因为我希望能够从两个以上的列表中生成许多排列的测试样本。 即使使用流,是否也会不可避免地成为一团糟呢?还是我不够用自己? 在意识到我正在寻找笛卡尔积之后,发现了一些