Skip to content

Implement Cartesian product of several collections by Java Stream

Right now I can only implement the Cartesian product of two collections, here is the code:

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

This code works fine in IntelliJ, but not in Eclipse. Both with compiler compliance level of 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>)

Here is

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 + ")";

    public boolean equals(Object o) {
        if(!(o instanceof Pair))
            return false;
        Pair p = (Pair) o;
            return false;
            return false;
        return true;

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

How to fix this error?

Is there an elegant way to implement the Cartesian product of several collections? Suppose we have class tuple.



Eclipse has problems with type inference. If you add a type hint .<Pair<T1,T2>>flatMap, it compiles fine.

If I may suggest a different approach, consider making your cartesianProduct not do the entire stream and collection but merely be a helper for flatMap:

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

Now you only need to create a Pair if you want the result to contains Pairs and you can do a higher-order cartesian product by applying flatMap several times:

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

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

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

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