任意数量集合的笛卡尔积

发布于 2024-07-16 06:45:22 字数 377 浏览 3 评论 0原文

您是否知道一些简洁的 Java 库可以让您生成两个(或更多)集合的笛卡尔积?

例如:我有三套。 第一个是 Person 类的对象,第二个是 Gift 类的对象,第三个是 GiftExtension 类的对象。

我想生成一组包含所有可能的三元组 Person-Gift-GiftExtension。

集合的数量可能会有所不同,因此我无法在嵌套的 foreach 循环中执行此操作。 在某些情况下,我的应用程序需要制作 Person-Gift 对的乘积,有时是三重 Person-Gift-GiftExtension,有时甚至可能有集合 Person-Gift-GiftExtension-GiftSecondExtension-GiftThirdExtension 等。

Do you know some neat Java libaries that allow you to make cartesian product of two (or more) sets?

For example: I have three sets. One with objects of class Person, second with objects of class Gift and third with objects of class GiftExtension.

I want to generate one set containing all possible triples Person-Gift-GiftExtension.

The number of sets might vary so I cannot do this in nested foreach loop.
Under some conditions my application needs to make a product of Person-Gift pair, sometimes it is triple Person-Gift-GiftExtension, sometimes there might even be sets Person-Gift-GiftExtension-GiftSecondExtension-GiftThirdExtension, etc.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

梦里梦着梦中梦 2024-07-23 06:45:23

基于索引的解决方案

使用索引是一种快速、内存高效且可以处理任意数量的集合的替代方案。 实现 Iterable 可以在 for-each 循环中轻松使用。 有关使用示例,请参阅#main 方法。

public class CartesianProduct implements Iterable<int[]>, Iterator<int[]> {

    private final int[] _lengths;
    private final int[] _indices;
    private boolean _hasNext = true;

    public CartesianProduct(int[] lengths) {
        _lengths = lengths;
        _indices = new int[lengths.length];
    }

    public boolean hasNext() {
        return _hasNext;
    }

    public int[] next() {
        int[] result = Arrays.copyOf(_indices, _indices.length);
        for (int i = _indices.length - 1; i >= 0; i--) {
            if (_indices[i] == _lengths[i] - 1) {
                _indices[i] = 0;
                if (i == 0) {
                    _hasNext = false;
                }
            } else {
                _indices[i]++;
                break;
            }
        }
        return result;
    }

    public Iterator<int[]> iterator() {
        return this;
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }

    /**
     * Usage example. Prints out
     * 
     * <pre>
     * [0, 0, 0] a, NANOSECONDS, 1
     * [0, 0, 1] a, NANOSECONDS, 2
     * [0, 0, 2] a, NANOSECONDS, 3
     * [0, 0, 3] a, NANOSECONDS, 4
     * [0, 1, 0] a, MICROSECONDS, 1
     * [0, 1, 1] a, MICROSECONDS, 2
     * [0, 1, 2] a, MICROSECONDS, 3
     * [0, 1, 3] a, MICROSECONDS, 4
     * [0, 2, 0] a, MILLISECONDS, 1
     * [0, 2, 1] a, MILLISECONDS, 2
     * [0, 2, 2] a, MILLISECONDS, 3
     * [0, 2, 3] a, MILLISECONDS, 4
     * [0, 3, 0] a, SECONDS, 1
     * [0, 3, 1] a, SECONDS, 2
     * [0, 3, 2] a, SECONDS, 3
     * [0, 3, 3] a, SECONDS, 4
     * [0, 4, 0] a, MINUTES, 1
     * [0, 4, 1] a, MINUTES, 2
     * ...
     * </pre>
     */
    public static void main(String[] args) {
        String[] list1 = { "a", "b", "c", };
        TimeUnit[] list2 = TimeUnit.values();
        int[] list3 = new int[] { 1, 2, 3, 4 };

        int[] lengths = new int[] { list1.length, list2.length, list3.length };
        for (int[] indices : new CartesianProduct(lengths)) {
            System.out.println(Arrays.toString(indices) //
                    + " " + list1[indices[0]] //
                    + ", " + list2[indices[1]] //
                    + ", " + list3[indices[2]]);
        }
    }
}

Index-based solution

Working with the indices is an alternative that is fast and memory-efficient and can handle any number of sets. Implementing Iterable allows easy use in a for-each loop. See the #main method for a usage example.

public class CartesianProduct implements Iterable<int[]>, Iterator<int[]> {

    private final int[] _lengths;
    private final int[] _indices;
    private boolean _hasNext = true;

    public CartesianProduct(int[] lengths) {
        _lengths = lengths;
        _indices = new int[lengths.length];
    }

    public boolean hasNext() {
        return _hasNext;
    }

    public int[] next() {
        int[] result = Arrays.copyOf(_indices, _indices.length);
        for (int i = _indices.length - 1; i >= 0; i--) {
            if (_indices[i] == _lengths[i] - 1) {
                _indices[i] = 0;
                if (i == 0) {
                    _hasNext = false;
                }
            } else {
                _indices[i]++;
                break;
            }
        }
        return result;
    }

    public Iterator<int[]> iterator() {
        return this;
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }

    /**
     * Usage example. Prints out
     * 
     * <pre>
     * [0, 0, 0] a, NANOSECONDS, 1
     * [0, 0, 1] a, NANOSECONDS, 2
     * [0, 0, 2] a, NANOSECONDS, 3
     * [0, 0, 3] a, NANOSECONDS, 4
     * [0, 1, 0] a, MICROSECONDS, 1
     * [0, 1, 1] a, MICROSECONDS, 2
     * [0, 1, 2] a, MICROSECONDS, 3
     * [0, 1, 3] a, MICROSECONDS, 4
     * [0, 2, 0] a, MILLISECONDS, 1
     * [0, 2, 1] a, MILLISECONDS, 2
     * [0, 2, 2] a, MILLISECONDS, 3
     * [0, 2, 3] a, MILLISECONDS, 4
     * [0, 3, 0] a, SECONDS, 1
     * [0, 3, 1] a, SECONDS, 2
     * [0, 3, 2] a, SECONDS, 3
     * [0, 3, 3] a, SECONDS, 4
     * [0, 4, 0] a, MINUTES, 1
     * [0, 4, 1] a, MINUTES, 2
     * ...
     * </pre>
     */
    public static void main(String[] args) {
        String[] list1 = { "a", "b", "c", };
        TimeUnit[] list2 = TimeUnit.values();
        int[] list3 = new int[] { 1, 2, 3, 4 };

        int[] lengths = new int[] { list1.length, list2.length, list3.length };
        for (int[] indices : new CartesianProduct(lengths)) {
            System.out.println(Arrays.toString(indices) //
                    + " " + list1[indices[0]] //
                    + ", " + list2[indices[1]] //
                    + ", " + list3[indices[2]]);
        }
    }
}
白昼 2024-07-23 06:45:23

组数可能会有所不同,所以我
无法在嵌套的 foreach 循环中执行此操作。

两个提示:

  • A x B x C = A x (B x C)
  • 递归

The number of sets might vary so I
cannot do this in nested foreach loop.

Two hints:

  • A x B x C = A x (B x C)
  • Recursion
痴情换悲伤 2024-07-23 06:45:23

这是一个 Iterable,它允许您使用简化的 for 循环:

import java.util.*;

// let's begin with the demo. Instead of Person and Gift, 
// I use the well known char and int. 
class CartesianIteratorTest {

    public static void main (String[] args) {
        List <Object> lc = Arrays.asList (new Object [] {'A', 'B', 'C', 'D'});
        List <Object> lC = Arrays.asList (new Object [] {'a', 'b', 'c'});   
        List <Object> li = Arrays.asList (new Object [] {1, 2, 3, 4});
            // sometimes, a generic solution like List <List <String>>
            // might be possible to use - typically, a mixture of types is 
            // the common nominator 
        List <List <Object>> llo = new ArrayList <List <Object>> ();
        llo.add (lc);
        llo.add (lC);
        llo.add (li);

        // Preparing the List of Lists is some work, but then ...    
        CartesianIterable <Object> ci = new CartesianIterable <Object> (llo);

        for (List <Object> lo: ci)
            show (lo);
    }

    public static void show (List <Object> lo) {
        System.out.print ("(");
        for (Object o: lo)
            System.out.print (o + ", ");
        System.out.println (")");
    }
}

它是如何完成的? 我们需要一个 Iterable,以使用简化的 for 循环,并且必须从 Iterable 返回一个 Iterator。
我们返回一个对象列表 - 这可能是一个 Set 而不是 List,但是 Set 没有索引访问,因此使用 Set 而不是 List 来实现它会更复杂一些。 如果不是通用解决方案,对象可以满足多种用途,但泛型允许更多限制。

class CartesianIterator <T> implements Iterator <List <T>> {

    private final List <List <T>> lilio;    
    private int current = 0;
    private final long last;

    public CartesianIterator (final List <List <T>> llo) {
        lilio = llo;
        long product = 1L;
        for (List <T> lio: lilio)
            product *= lio.size ();
        last = product;
    } 

    public boolean hasNext () {
        return current != last;
    }

    public List <T> next () {
        ++current;
        return get (current - 1, lilio);
    }

    public void remove () {
        ++current;
    }

    private List<T> get (final int n, final List <List <T>> lili) {
        switch (lili.size ())
        {
            case 0: return new ArrayList <T> (); // no break past return;
            default: {
                List <T> inner = lili.get (0);
                List <T> lo = new ArrayList <T> ();
                lo.add (inner.get (n % inner.size ()));
                lo.addAll (get (n / inner.size (), lili.subList (1, lili.size ())));
                return lo;
            }
        }
    }
}

数学工作是通过“get”方法完成的。 考虑 2 组 10 个元素。 您总共有 100 种组合,从 00、01、02、... 10、... 到 99。对于 5 X 10 元素,有 50 种组合;对于 2 X 3 元素,有 6 种组合。 子列表大小的模有助于为每次迭代选择一个元素。

Iterable是这里最不有趣的事情:

class CartesianIterable <T> implements Iterable <List <T>> {

    private List <List <T>> lilio;  

    public CartesianIterable (List <List <T>> llo) {
        lilio = llo;
    }

    public Iterator <List <T>> iterator () {
        return new CartesianIterator <T> (lilio);
    }
}

为了实现Iterable,它允许for-each类型的循环,我们必须实现iterator(),对于Iterator我们必须实现hasNext()、next()和remove()。

结果:

(A, a, 1, )
(B, a, 1, )
(C, a, 1, )
(D, a, 1, )
(A, b, 1, )
(B, b, 1, )
(C, b, 1, )
(D, b, 1, )
...
(A, a, 2, )
...
(C, c, 4, )
(D, c, 4, )

Here is an Iterable, which allows you to use a simplified for-loop:

import java.util.*;

// let's begin with the demo. Instead of Person and Gift, 
// I use the well known char and int. 
class CartesianIteratorTest {

    public static void main (String[] args) {
        List <Object> lc = Arrays.asList (new Object [] {'A', 'B', 'C', 'D'});
        List <Object> lC = Arrays.asList (new Object [] {'a', 'b', 'c'});   
        List <Object> li = Arrays.asList (new Object [] {1, 2, 3, 4});
            // sometimes, a generic solution like List <List <String>>
            // might be possible to use - typically, a mixture of types is 
            // the common nominator 
        List <List <Object>> llo = new ArrayList <List <Object>> ();
        llo.add (lc);
        llo.add (lC);
        llo.add (li);

        // Preparing the List of Lists is some work, but then ...    
        CartesianIterable <Object> ci = new CartesianIterable <Object> (llo);

        for (List <Object> lo: ci)
            show (lo);
    }

    public static void show (List <Object> lo) {
        System.out.print ("(");
        for (Object o: lo)
            System.out.print (o + ", ");
        System.out.println (")");
    }
}

How is it done? We need an Iterable, to use the simplified for-loop, and an Iterator has to be returned from the Iterable.
We return a List of Objects - this could be a Set instead of List, but Set has no indexed access, so it would be a bit more complicated, to implement it with Set instead of List. Instead of a generic solution, Object would have been fine for many purposes, but generics allow for more restrictions.

class CartesianIterator <T> implements Iterator <List <T>> {

    private final List <List <T>> lilio;    
    private int current = 0;
    private final long last;

    public CartesianIterator (final List <List <T>> llo) {
        lilio = llo;
        long product = 1L;
        for (List <T> lio: lilio)
            product *= lio.size ();
        last = product;
    } 

    public boolean hasNext () {
        return current != last;
    }

    public List <T> next () {
        ++current;
        return get (current - 1, lilio);
    }

    public void remove () {
        ++current;
    }

    private List<T> get (final int n, final List <List <T>> lili) {
        switch (lili.size ())
        {
            case 0: return new ArrayList <T> (); // no break past return;
            default: {
                List <T> inner = lili.get (0);
                List <T> lo = new ArrayList <T> ();
                lo.add (inner.get (n % inner.size ()));
                lo.addAll (get (n / inner.size (), lili.subList (1, lili.size ())));
                return lo;
            }
        }
    }
}

The mathematical work is done in the 'get'-method. Think about 2 sets of 10 elements. You have a total of 100 combinations, enumerated from 00, 01, 02, ... 10, ... to 99. For 5 X 10 elements 50, for 2 X 3 elements 6 combinations. The modulo of the sublist size helps to pick one element for each iteration.

Iterable i the least interesting thing here:

class CartesianIterable <T> implements Iterable <List <T>> {

    private List <List <T>> lilio;  

    public CartesianIterable (List <List <T>> llo) {
        lilio = llo;
    }

    public Iterator <List <T>> iterator () {
        return new CartesianIterator <T> (lilio);
    }
}

To implement Iterable, which allows the for-each kind of loop, we have to implement iterator (), and for Iterator we have to implement hasNext (), next () and remove ().

Result:

(A, a, 1, )
(B, a, 1, )
(C, a, 1, )
(D, a, 1, )
(A, b, 1, )
(B, b, 1, )
(C, b, 1, )
(D, b, 1, )
...
(A, a, 2, )
...
(C, c, 4, )
(D, c, 4, )
风流物 2024-07-23 06:45:23

这是一个迭代器,它给出二维数组的笛卡尔积,其中数组组件代表问题中的集合(人们总是可以将实际的集合转换为数组):

public class CartesianIterator<T> implements Iterator<T[]> {
    private final T[][] sets;
    private final IntFunction<T[]> arrayConstructor;

    private int count = 0;
    private T[] next = null;

    public CartesianIterator(T[][] sets, IntFunction<T[]> arrayConstructor) {
        Objects.requireNonNull(sets);
        Objects.requireNonNull(arrayConstructor);

        this.sets = copySets(sets);
        this.arrayConstructor = arrayConstructor;
    }

    private static <T> T[][] copySets(T[][] sets) {
        // If any of the arrays are empty, then the entire iterator is empty.
        // This prevents division by zero in `hasNext`.
        for (T[] set : sets) {
            if (set.length == 0) {
                return Arrays.copyOf(sets, 0);
            }
        }
        return sets.clone();
    }

    @Override
    public boolean hasNext() {
        if (next != null) {
            return true;
        }

        int tmp = count;
        T[] value = arrayConstructor.apply(sets.length);
        for (int i = 0; i < value.length; i++) {
            T[] set = sets[i];

            int radix = set.length;
            int index = tmp % radix;

            value[i] = set[index];

            tmp /= radix;
        }

        if (tmp != 0) {
            // Overflow.
            return false;
        }

        next = value;
        count++;

        return true;
    }

    @Override
    public T[] next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        T[] tmp = next;
        next = null;
        return tmp;
    }
}

基本思想是将 count 视为多基数(数字 i 有自己的基数,等于 i 的长度第一个“集合”)。 每当我们必须解析 next 时(即,当调用 hasNext()nextnull 时),我们将这个数字分解成这个多基数中的数字。 这些数字现在用作我们从不同集合中提取元素的索引。

使用示例:

String[] a = { "a", "b", "c"};
String[] b = { "X" };
String[] c = { "r", "s" };

String[][] abc = { a, b, c };

Iterable<String[]> it = () -> new CartesianIterator<>(abc, String[]::new);
for (String[] s : it) {
    System.out.println(Arrays.toString(s));
}

输出:

[a, X, r]
[b, X, r]
[c, X, r]
[a, X, s]
[b, X, s]
[c, X, s]

如果不喜欢数组,则代码可以轻松转换为使用集合。

我想这或多或少类似于“用户未知”给出的答案,只是没有递归和集合。

Here is an Iterator that gives the cartesian product of a two-dimensional array, where the arrays components represent the sets from the question (one can always convert actual Sets to arrays):

public class CartesianIterator<T> implements Iterator<T[]> {
    private final T[][] sets;
    private final IntFunction<T[]> arrayConstructor;

    private int count = 0;
    private T[] next = null;

    public CartesianIterator(T[][] sets, IntFunction<T[]> arrayConstructor) {
        Objects.requireNonNull(sets);
        Objects.requireNonNull(arrayConstructor);

        this.sets = copySets(sets);
        this.arrayConstructor = arrayConstructor;
    }

    private static <T> T[][] copySets(T[][] sets) {
        // If any of the arrays are empty, then the entire iterator is empty.
        // This prevents division by zero in `hasNext`.
        for (T[] set : sets) {
            if (set.length == 0) {
                return Arrays.copyOf(sets, 0);
            }
        }
        return sets.clone();
    }

    @Override
    public boolean hasNext() {
        if (next != null) {
            return true;
        }

        int tmp = count;
        T[] value = arrayConstructor.apply(sets.length);
        for (int i = 0; i < value.length; i++) {
            T[] set = sets[i];

            int radix = set.length;
            int index = tmp % radix;

            value[i] = set[index];

            tmp /= radix;
        }

        if (tmp != 0) {
            // Overflow.
            return false;
        }

        next = value;
        count++;

        return true;
    }

    @Override
    public T[] next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        T[] tmp = next;
        next = null;
        return tmp;
    }
}

The basic idea is to treat count as a multi-radix number (digit i has its own radix which equals the length of the i'th "set"). Whenever we have to resolve next (that is, when hasNext() is called and next is null), we decompose the number into its digits in this multi-radix. These digits are now used as the indices from which we draw elements from the different sets.

Example use:

String[] a = { "a", "b", "c"};
String[] b = { "X" };
String[] c = { "r", "s" };

String[][] abc = { a, b, c };

Iterable<String[]> it = () -> new CartesianIterator<>(abc, String[]::new);
for (String[] s : it) {
    System.out.println(Arrays.toString(s));
}

Output:

[a, X, r]
[b, X, r]
[c, X, r]
[a, X, s]
[b, X, s]
[c, X, s]

If one doesn't like arrays, the code is trivially convertible into using collections.

I guess this is more or less similar to the answer given by "user unknown", only without recursion and collections.

风吹雪碎 2024-07-23 06:45:23

您可以获取任意数量的不同类型集合的笛卡尔积并将其存储到对象Set<的集合集合中;使用 Java 9 Streams 设置>,如下所示:

在线试用!

public static Set<Set<Object>> cartesianProduct(Set<?>... sets) {
    // incorrect incoming data
    if (sets == null) return Collections.emptySet();
    return Arrays.stream(sets)
            // non-null and non-empty sets
            .filter(set -> set != null && set.size() > 0)
            // represent each set element as Set<Object>
            .map(set -> set.stream().map(Set::<Object>of)
                    // Stream<Set<Set<Object>>>
                    .collect(Collectors.toSet()))
            // summation of pairs of inner sets
            .reduce((set1, set2) -> set1.stream()
                    // combinations of inner sets
                    .flatMap(inner1 -> set2.stream()
                            // merge two inner sets into one
                            .map(inner2 -> Stream.of(inner1, inner2)
                                    .flatMap(Set::stream)
                                    .collect(Collectors.toCollection(
                                            LinkedHashSet::new))))
                    // set of combinations
                    .collect(Collectors.toCollection(LinkedHashSet::new)))
            // returns Set<Set<Object>>, otherwise an empty set
            .orElse(Collections.emptySet());
}
public static void main(String[] args) {
    Set<Integer> set1 = Set.of(1, 2, 3);
    Set<String> set2 = Set.of("A", "B", "C");
    Set<Object> set3 = Set.of(new Time(0));

    Set<Set<Object>> sets = cartesianProduct(set1, set2, set3);
    // output
    sets.forEach(System.out::println);
}

输出:

[1, A, 03:00:00]
[1, B, 03:00:00]
[1, C, 03:00:00]
[2, A, 03:00:00]
[2, B, 03:00:00]
[2, C, 03:00:00]
[3, A, 03:00:00]
[3, B, 03:00:00]
[3, C, 03:00:00]

另请参阅:如何创建数据类似于三个不同类型列表的笛卡尔积的结构?

You can get the Cartesian product of an arbitrary number of sets of different types and store it into a set of sets of objects Set<Set<Object>> using Java 9 Streams as follows:

Try it online!

public static Set<Set<Object>> cartesianProduct(Set<?>... sets) {
    // incorrect incoming data
    if (sets == null) return Collections.emptySet();
    return Arrays.stream(sets)
            // non-null and non-empty sets
            .filter(set -> set != null && set.size() > 0)
            // represent each set element as Set<Object>
            .map(set -> set.stream().map(Set::<Object>of)
                    // Stream<Set<Set<Object>>>
                    .collect(Collectors.toSet()))
            // summation of pairs of inner sets
            .reduce((set1, set2) -> set1.stream()
                    // combinations of inner sets
                    .flatMap(inner1 -> set2.stream()
                            // merge two inner sets into one
                            .map(inner2 -> Stream.of(inner1, inner2)
                                    .flatMap(Set::stream)
                                    .collect(Collectors.toCollection(
                                            LinkedHashSet::new))))
                    // set of combinations
                    .collect(Collectors.toCollection(LinkedHashSet::new)))
            // returns Set<Set<Object>>, otherwise an empty set
            .orElse(Collections.emptySet());
}
public static void main(String[] args) {
    Set<Integer> set1 = Set.of(1, 2, 3);
    Set<String> set2 = Set.of("A", "B", "C");
    Set<Object> set3 = Set.of(new Time(0));

    Set<Set<Object>> sets = cartesianProduct(set1, set2, set3);
    // output
    sets.forEach(System.out::println);
}

Output:

[1, A, 03:00:00]
[1, B, 03:00:00]
[1, C, 03:00:00]
[2, A, 03:00:00]
[2, B, 03:00:00]
[2, C, 03:00:00]
[3, A, 03:00:00]
[3, B, 03:00:00]
[3, C, 03:00:00]

See also: How to create a data structure similar to the cartesian product of three lists of different types?

好菇凉咱不稀罕他 2024-07-23 06:45:23

是的,有函数式 Java

对于集合 s

s.bind(P.p2(), s);

Yes, there is Functional Java.

For a set s:

s.bind(P.p2(), s);
一个人的旅程 2024-07-23 06:45:23

例如,对于整数集,一个简单的解决方案应如下所示:

void printCombination(List<Set<Integer>> listSet, Set<Integer> combination) {
    if (listSet.isEmpty()) {
        System.out.println("a combination :" + combination);

        return;
    }

    Set<Integer> intSet = listSet.get(0);
    for (Integer it : intSet) {
        Set<Integer> combination1 = new HashSet<Integer>();
        combination1.addAll(combination);
        combination1.add(it);

        List<Set<Integer>> listSet1 = new ArrayList<Set<Integer>>();
        listSet1.addAll(listSet);
        listSet1.remove(0);
        this.printCombination(listSet1, combination1);
    }

} 

a simple solution, for example, for Integer set should be as follows:

void printCombination(List<Set<Integer>> listSet, Set<Integer> combination) {
    if (listSet.isEmpty()) {
        System.out.println("a combination :" + combination);

        return;
    }

    Set<Integer> intSet = listSet.get(0);
    for (Integer it : intSet) {
        Set<Integer> combination1 = new HashSet<Integer>();
        combination1.addAll(combination);
        combination1.add(it);

        List<Set<Integer>> listSet1 = new ArrayList<Set<Integer>>();
        listSet1.addAll(listSet);
        listSet1.remove(0);
        this.printCombination(listSet1, combination1);
    }

} 
独﹏钓一江月 2024-07-23 06:45:23

您可以通过此应用一个名为Seq的简单生成器接口。 与 不同Guava 的 cartesianProduct,集合/列表/迭代不需要位于相同的泛型类型绑定 B 内,所有类型都受欢迎。 您所需要做的就是将乘积编写为普通的嵌套 for 循环。

public static void main(String[] args) {
    List<String> ls1 = Arrays.asList("a", "b");
    List<Integer> ls2 = Arrays.asList(1, 2);
    List<Character> ls3 = Arrays.asList('x', 'y');

    Seq<String> seq = c -> {
        for (String s : ls1) {
            for (Integer i : ls2) {
                for (Character d : ls3) {
                    c.accept(s + i + d);
                }
            }
        }
    };

    System.out.println(seq.toSet());
}

结果将是

[a2x, a1y, b1x, b2y, a1x, a2y, b2x, b1y]

You can apply a simple generator interferace named Seq via this library. Unlike Guava's cartesianProduct, the sets/lists/iterables do not need to be within the same generic type bound B, all types are welcome. And all you need to do is writing the product as normal nested for-loops.

public static void main(String[] args) {
    List<String> ls1 = Arrays.asList("a", "b");
    List<Integer> ls2 = Arrays.asList(1, 2);
    List<Character> ls3 = Arrays.asList('x', 'y');

    Seq<String> seq = c -> {
        for (String s : ls1) {
            for (Integer i : ls2) {
                for (Character d : ls3) {
                    c.accept(s + i + d);
                }
            }
        }
    };

    System.out.println(seq.toSet());
}

The result would be

[a2x, a1y, b1x, b2y, a1x, a2y, b2x, b1y]
梦冥 2024-07-23 06:45:22

编辑:删除了之前的两套解决方案。 有关详细信息,请参阅编辑历史记录。

这是一种对任意数量的集合递归执行此操作的方法:

public static Set<Set<Object>> cartesianProduct(Set<?>... sets) {
    if (sets.length < 2)
        throw new IllegalArgumentException(
                "Can't have a product of fewer than two sets (got " +
                sets.length + ")");

    return _cartesianProduct(0, sets);
}

private static Set<Set<Object>> _cartesianProduct(int index, Set<?>... sets) {
    Set<Set<Object>> ret = new HashSet<Set<Object>>();
    if (index == sets.length) {
        ret.add(new HashSet<Object>());
    } else {
        for (Object obj : sets[index]) {
            for (Set<Object> set : _cartesianProduct(index+1, sets)) {
                set.add(obj);
                ret.add(set);
            }
        }
    }
    return ret;
}

请注意,不可能在返回的集合中保留任何通用类型信息。 如果您事先知道要获取多少个集合的乘积,则可以定义一个通用元组来保存那么多元素(例如 Triple),但是有Java 中无法拥有任意数量的泛型参数。

Edit: Previous solutions for two sets removed. See edit history for details.

Here is a way to do it recursively for an arbitrary number of sets:

public static Set<Set<Object>> cartesianProduct(Set<?>... sets) {
    if (sets.length < 2)
        throw new IllegalArgumentException(
                "Can't have a product of fewer than two sets (got " +
                sets.length + ")");

    return _cartesianProduct(0, sets);
}

private static Set<Set<Object>> _cartesianProduct(int index, Set<?>... sets) {
    Set<Set<Object>> ret = new HashSet<Set<Object>>();
    if (index == sets.length) {
        ret.add(new HashSet<Object>());
    } else {
        for (Object obj : sets[index]) {
            for (Set<Object> set : _cartesianProduct(index+1, sets)) {
                set.add(obj);
                ret.add(set);
            }
        }
    }
    return ret;
}

Note that it is impossible to keep any generic type information with the returned sets. If you knew in advance how many sets you wanted to take the product of, you could define a generic tuple to hold that many elements (for instance Triple<A, B, C>), but there is no way to have an arbitrary number of generic parameters in Java.

聆听风音 2024-07-23 06:45:22

这是一个非常老的问题,但为什么不使用 Guava 的笛卡尔积

This is a pretty old question, but why not use Guava's cartesianProduct?

挽梦忆笙歌 2024-07-23 06:45:22

下面的方法创建字符串列表列表的笛卡尔积:

protected <T> List<List<T>> cartesianProduct(List<List<T>> lists) {
    List<List<T>> resultLists = new ArrayList<List<T>>();
    if (lists.size() == 0) {
        resultLists.add(new ArrayList<T>());
        return resultLists;
    } else {
        List<T> firstList = lists.get(0);
        List<List<T>> remainingLists = cartesianProduct(lists.subList(1, lists.size()));
        for (T condition : firstList) {
            for (List<T> remainingList : remainingLists) {
                ArrayList<T> resultList = new ArrayList<T>();
                resultList.add(condition);
                resultList.addAll(remainingList);
                resultLists.add(resultList);
            }
        }
    }
    return resultLists;
}

示例:

System.out.println(cartesianProduct(Arrays.asList(Arrays.asList("Apple", "Banana"), Arrays.asList("Red", "Green", "Blue"))));

将产生以下结果:

[[Apple, Red], [Apple, Green], [Apple, Blue], [Banana, Red], [Banana, Green], [Banana, Blue]]

The method below creates the cartesian product of a list of list of strings:

protected <T> List<List<T>> cartesianProduct(List<List<T>> lists) {
    List<List<T>> resultLists = new ArrayList<List<T>>();
    if (lists.size() == 0) {
        resultLists.add(new ArrayList<T>());
        return resultLists;
    } else {
        List<T> firstList = lists.get(0);
        List<List<T>> remainingLists = cartesianProduct(lists.subList(1, lists.size()));
        for (T condition : firstList) {
            for (List<T> remainingList : remainingLists) {
                ArrayList<T> resultList = new ArrayList<T>();
                resultList.add(condition);
                resultList.addAll(remainingList);
                resultLists.add(resultList);
            }
        }
    }
    return resultLists;
}

Example:

System.out.println(cartesianProduct(Arrays.asList(Arrays.asList("Apple", "Banana"), Arrays.asList("Red", "Green", "Blue"))));

would yield this:

[[Apple, Red], [Apple, Green], [Apple, Blue], [Banana, Red], [Banana, Green], [Banana, Blue]]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文