cyberangles blog

How to Compute Cartesian Product of Arbitrary Sets in Java: Top Libraries for Dynamic Multi-Set Combinations

The Cartesian product of sets is a fundamental concept in mathematics and computer science, representing all possible combinations of elements from multiple sets. For example, if you have sets ( A = {a, b} ) and ( B = {1, 2} ), their Cartesian product is ( A \times B = {(a,1), (a,2), (b,1), (b,2)} ). This becomes even more powerful when extended to arbitrary sets (three or more), enabling use cases like generating test data, combinatorial search, or dynamic filtering in applications.

While Java is a robust language, it lacks a built-in utility for computing the Cartesian product of arbitrary sets. Implementing this manually is error-prone, especially for dynamic numbers of sets, and requires handling edge cases like empty sets or large datasets. To solve this, developers can leverage specialized libraries that abstract the complexity.

In this blog, we’ll explore the top Java libraries for computing the Cartesian product of arbitrary sets, with practical examples, setup guides, and comparisons to help you choose the right tool for your needs.

2025-11

Table of Contents#

  1. Understanding the Cartesian Product
  2. Challenges of Manual Implementation in Java
  3. Top Libraries for Cartesian Product in Java
  4. Comparison of Libraries
  5. Conclusion
  6. References

1. Understanding the Cartesian Product#

Formally, the Cartesian product of a collection of sets ( S_1, S_2, ..., S_n ) is the set of all ordered tuples ( (x_1, x_2, ..., x_n) ) where ( x_i \in S_i ) for each ( i ). For example:

  • Sets: ( S_1 = {a, b}, S_2 = {1, 2}, S_3 = {x, y} )
  • Cartesian Product: ( S_1 \times S_2 \times S_3 = {(a,1,x), (a,1,y), (a,2,x), (a,2,y), (b,1,x), (b,1,y), (b,2,x), (b,2,y)} )

The size of the product is the product of the sizes of the input sets (( |S_1| \times |S_2| \times ... \times |S_n| )), making it computationally intensive for large sets. Libraries optimize this by using iterators or lazy evaluation to avoid loading all combinations into memory at once.

2. Challenges of Manual Implementation in Java#

Implementing the Cartesian product manually in Java is non-trivial, especially for arbitrary numbers of sets (not just 2 or 3). Key challenges include:

  • Dynamic Arity Handling: Supporting ( n ) sets (where ( n ) is unknown at compile time) requires recursive logic or complex iteration over indices.
  • Memory Efficiency: Generating all combinations upfront can cause out-of-memory errors for large sets; lazy iteration is needed.
  • Type Safety: Ensuring the output tuples retain type information (e.g., avoiding raw Object[] arrays) is tricky with vanilla Java.
  • Edge Cases: Handling empty sets (product is empty), singleton sets, or mixed data types.

These challenges make libraries the preferred choice for production code.

3. Top Libraries for Cartesian Product in Java#

Let’s explore three leading libraries to compute the Cartesian product, with setup instructions, examples, and pros/cons.

3.1 Apache Commons Collections#

Overview: Apache Commons Collections is a mature, widely used library that extends Java’s standard collections. It includes CartesianProductIterator, a utility for generating Cartesian products of arbitrary sets.

Setup#

Add the Maven dependency:

<dependency>  
    <groupId>org.apache.commons</groupId>  
    <artifactId>commons-collections4</artifactId>  
    <version>4.4</version> <!-- Check for latest version -->  
</dependency>  

Example: Cartesian Product of Arbitrary Sets#

import org.apache.commons.collections4.iterators.CartesianProductIterator;  
import java.util.Arrays;  
import java.util.List;  
import java.util.Set;  
import java.util.stream.Collectors;  
 
public class ApacheCommonsExample {  
    public static void main(String[] args) {  
        // Define arbitrary sets (could be 2, 3, or more)  
        Set<String> colors = Set.of("red", "blue");  
        Set<Integer> sizes = Set.of(10, 20);  
        Set<Character> styles = Set.of('A', 'B');  
 
        // Wrap sets in a list (order matters for tuples)  
        List<Set<?>> sets = Arrays.asList(colors, sizes, styles);  
 
        // Create CartesianProductIterator  
        CartesianProductIterator<?> iterator = new CartesianProductIterator<>(sets);  
 
        // Convert iterator to a list of tuples (each tuple is an Object[])  
        List<List<?>> product = iterator.stream()  
                .map(Arrays::asList) // Convert Object[] to List<?>  
                .collect(Collectors.toList());  
 
        // Print result  
        System.out.println("Cartesian Product: " + product);  
    }  
}  

Output:

Cartesian Product: [[red, 10, A], [red, 10, B], [red, 20, A], [red, 20, B], [blue, 10, A], [blue, 10, B], [blue, 20, A], [blue, 20, B]]  

Pros:#

  • Mature & Reliable: Used in countless projects; battle-tested.
  • Handles Arbitrary Sets: Works with 2, 3, or more sets.
  • Memory Efficient: Uses lazy iteration (doesn’t load all tuples into memory at once).

Cons:#

  • Verbose: Requires manual conversion from Iterator<Object[]> to a list of lists.
  • No Type Safety: Returns Object[] arrays, so casting is needed for type-specific operations.

3.2 Vavr (formerly Javaslang)#

Overview: Vavr is a functional programming library for Java that introduces immutable collections and utilities. It provides a concise cartesianProduct method for sequences.

Setup#

Add the Maven dependency:

<dependency>  
    <groupId>io.vavr</groupId>  
    <artifactId>vavr</artifactId>  
    <version>0.10.4</version> <!-- Check for latest version -->  
</dependency>  

Example: Cartesian Product with Vavr#

Vavr’s Seq.cartesianProduct method takes a collection of Iterables and returns a List of Tuples (type-safe tuples like Tuple2, Tuple3, etc.).

import io.vavr.Tuple3;  
import io.vavr.collection.List;  
 
public class VavrExample {  
    public static void main(String[] args) {  
        // Define sets using Vavr's immutable List (works with any Iterable)  
        List<String> colors = List.of("red", "blue");  
        List<Integer> sizes = List.of(10, 20);  
        List<Character> styles = List.of('A', 'B');  
 
        // Wrap sets in a Vavr List (order defines tuple structure)  
        List<List<?>> sets = List.of(colors, sizes, styles);  
 
        // Compute Cartesian product (returns List<Tuple3<String, Integer, Character>>)  
        List<Tuple3<String, Integer, Character>> product = sets.cartesianProduct()  
                .map(tuple -> tuple.map(0, String.class)   // Cast elements to specific types  
                                   .map(1, Integer.class)  
                                   .map(2, Character.class));  
 
        // Print result  
        System.out.println("Cartesian Product: " + product);  
    }  
}  

Output:

Cartesian Product: List((red, 10, A), (red, 10, B), (red, 20, A), (red, 20, B), (blue, 10, A), (blue, 10, B), (blue, 20, A), (blue, 20, B))  

Pros:#

  • Type Safety: Returns TupleN objects with compile-time type checks.
  • Functional Style: Integrates with Vavr’s immutable collections and streams.
  • Concise: One-line product computation with cartesianProduct().

Cons:#

  • Adds a Large Dependency: Vavr is a full-featured functional library; overkill if only needing Cartesian product.
  • Learning Curve: Requires familiarity with Vavr’s API (e.g., List vs. Java’s List).

3.3 jOOλ (jOOQ’s Lambda Library)#

Overview: jOOλ is a lightweight library that extends Java 8+ streams with functional utilities. It includes Seq.crossJoin, which computes the Cartesian product of sequences.

Setup#

Add the Maven dependency:

<dependency>  
    <groupId>org.jooq</groupId>  
    <artifactId>jool</artifactId>  
    <version>0.9.14</version> <!-- Check for latest version -->  
</dependency>  

Example: Cartesian Product with jOOλ#

jOOλ’s Seq.crossJoin takes varargs of Seq (stream-like sequences) and returns a Seq of Tuples.

import org.jooq.lambda.Seq;  
import org.jooq.lambda.tuple.Tuple3;  
 
import java.util.Set;  
 
public class JoolExample {  
    public static void main(String[] args) {  
        // Define sets (convert to jOOλ Seq for crossJoin)  
        Seq<String> colors = Seq.seq(Set.of("red", "blue"));  
        Seq<Integer> sizes = Seq.seq(Set.of(10, 20));  
        Seq<Character> styles = Seq.seq(Set.of('A', 'B'));  
 
        // Compute Cartesian product (crossJoin)  
        Seq<Tuple3<String, Integer, Character>> product = Seq.crossJoin(colors, sizes, styles);  
 
        // Print result  
        product.forEach(System.out::println);  
    }  
}  

Output:

(red, 10, A)  
(red, 10, B)  
(red, 20, A)  
(red, 20, B)  
(blue, 10, A)  
(blue, 10, B)  
(blue, 20, A)  
(blue, 20, B)  

Pros:#

  • Lightweight: Small dependency (~200KB) focused on lambda utilities.
  • Stream Integration: Returns a Seq (compatible with Java streams).
  • Type-Safe Tuples: Uses TupleN with generic type parameters.

Cons:#

  • Limited to Fixed Arity: crossJoin requires explicit arguments (e.g., crossJoin(s1, s2, s3)), not arbitrary sets (workaround: use crossJoin(Seq.of(sets)) for dynamic sets).

4. Comparison of Libraries#

FeatureApache Commons CollectionsVavrjOOλ
SetupSimple (Maven/Gradle)Simple (Maven/Gradle)Simple (Maven/Gradle)
API StyleImperative (Iterator-based)Functional (immutable collections)Functional (stream-based)
Type SafetyNo (returns Object[])Yes (type-safe TupleN)Yes (type-safe TupleN)
Dynamic SetsYes (accepts List<Set<?>>)Yes (accepts List<Iterable<?>>)Limited (varargs or Seq<Seq<?>>)
Memory EfficiencyHigh (lazy iterator)High (lazy evaluation)High (stream-based)
Additional FeaturesExtensive collection utilitiesFull functional programming toolkitStream extensions, lambda utilities

5. Conclusion#

  • Choose Apache Commons Collections if you need a mature, no-frills solution with broad compatibility.
  • Choose Vavr for a functional, type-safe approach with immutable collections (ideal for functional programming projects).
  • Choose jOOλ for lightweight stream integration and type-safe tuples (great for projects already using Java streams).

All three libraries eliminate the complexity of manual Cartesian product implementation, letting you focus on business logic. For most use cases, Apache Commons or jOOλ are excellent starting points, while Vavr shines in functional codebases.

6. References#