Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cartesian product of streams in Java 8 as stream (using streams only)

Tags:

People also ask

What is stream () method in Java?

A stream consists of source followed by zero or more intermediate methods combined together (pipelined) and a terminal method to process the objects obtained from the source as per the methods described. Stream is used to compute elements as per the pipelined methods without altering the original value of the object.

What are the two types of streams proposed by Java 8?

What are the two types of Streams offered by java 8? Explanation: Sequential stream and parallel stream are two types of stream provided by java.

What are the methods in streams in Java 8?

With Java 8, Collection interface has two methods to generate a Stream. stream() − Returns a sequential stream considering collection as its source. parallelStream() − Returns a parallel Stream considering collection as its source.

Does Java 8 support streams?

Java 8 offers the possibility to create streams out of three primitive types: int, long and double. As Stream<T> is a generic interface, and there is no way to use primitives as a type parameter with generics, three new special interfaces were created: IntStream, LongStream, DoubleStream.


I would like to create a method which creates a stream of elements which are cartesian products of multiple given streams (aggregated to the same type at the end by a binary operator). Please note that both arguments and results are streams, not collections.

For example, for two streams of {A, B} and {X, Y} I would like it produce stream of values {AX, AY, BX, BY} (simple concatenation is used for aggregating the strings). So far, I have came up with this code:

private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator, Stream<T>... streams) {
    Stream<T> result = null;

    for (Stream<T> stream : streams) {
        if (result == null) {
            result = stream;
        } else {
            result = result.flatMap(m -> stream.map(n -> aggregator.apply(m, n)));
        }
    }

    return result;
}

This is my desired use case:

Stream<String> result = cartesian(
  (a, b) -> a + b, 
  Stream.of("A", "B"), 
  Stream.of("X", "Y")
);

System.out.println(result.collect(Collectors.toList()));

Expected result: AX, AY, BX, BY.

Another example:

Stream<String> result = cartesian(
  (a, b) -> a + b, 
  Stream.of("A", "B"), 
  Stream.of("K", "L"), 
  Stream.of("X", "Y")
);

Expected result: AKX, AKY, ALX, ALY, BKX, BKY, BLX, BLY.

However, if I run the code, I get this error:

IllegalStateException: stream has already been operated upon or closed

Where is the stream consumed? By flatMap? Can it be easily fixed?