permutate two streams without the need of materialization

I have two streams which I must materialize into two lists to get the permutations from both streams:

public Stream<Permutation> getAll() {

   Stream.Builder<Permutation> all = Stream.builder();
   
   // unfortunately, I must collect it into a list
   var list1 = IntStream.iterate(0, d -> d - 1).limit(1000000).boxed().collect(Collectors.toList());
   var list2 = IntStream.iterate(0, d -> d + 1).limit(1000000).boxed().collect(Collectors.toList());
   
   // I must use a classic loop (and cannot operate on those streams) to  avoid java.lang.IllegalStateException
   for(var l1: list1) {
       for(var l2: list2) {
           // the permutation class consists of two int properties
           all.add(new Permutation(l1, l2));
       }
   }
   return all.build();
}

Is there a way to avoid to materialize list1 and list2 and operate only on those streams to return the permutations? I have tried it but I get

java.lang.IllegalStateException: stream has already been operated upon or closed

Therefore I have materialized the two lists and used a classic loop. However, I would like to improve the performance by doing the following steps:

  • avoid the materialization of list1 and list2
  • and maybe also use parallelStream for list1 and list2 to get the permutations faster

Is this possible? If so, how?

EDIT:

Thanks to @Andreas for the solution which works so far. However, I wonder how I can create a permutation from two getAll()-streams without the need to materialize it in between:

// The `Permutations` class holds two `Permuation`-instances.
Stream<Permutations> allPermutations(){

    Stream<Permutation> stream1 = getAll();
    Stream<Permutation> stream2 = getAll();
 
    // returns java.lang.IllegalStateException: stream has already been operated upon or closed
    return stream1.flatMap(s1->stream2.map(s2->new Permutations(s1,s2));

}

Answer

You can do it like this:

public Stream<Permutation> getAll() {
    return IntStream.iterate(0, d -> d - 1).limit(1000000).boxed()
            .flatMap(l1 -> IntStream.iterate(0, d -> d + 1).limit(1000000)
                                    .mapToObj(l2 -> new Permutation(l1, l2)));
}

The caller can decide whether or not to use parallel processing:

// Sequential
Stream<Permutation> stream = getAll();

// Parallel
Stream<Permutation> stream = getAll().parallel();

No need to call sequential(), since iterate() returns a new sequential IntStream.

Leave a Reply

Your email address will not be published. Required fields are marked *