fps
fps

Reputation: 34460

Behavior of Stream.generate method when passed a Supplier<Stream<T>> argument

After reading this question and one of its answers, I decided to run some tests on my own. To my surprise, I came to a situation that looks quite strange.

Here's the code:

public class InfiniteRepeatingStream {

    public static void main(String[] args) {

        Stream<Integer> s1 = repeatFromSupplier(() -> Stream.of(1, 2, 3));
        s1.sequential().limit(10).forEachOrdered(System.out::print);

        System.out.print("\n----------------\n");

        Stream<Integer> s2 = repeatFromStream(Stream.of(1, 2, 3));
        s2.sequential().limit(10).forEachOrdered(System.out::print);
    }

    static <T> Stream<T> repeatFromSupplier(Supplier<Stream<T>> supplier) {
        return Stream.generate(supplier).flatMap(s -> s);
    }

    static <T> Stream<T> repeatFromStream(Stream<T> stream) {
        return Stream.generate(() -> stream).flatMap(s -> s);
    }
}

As you see, the difference between the repeatFromSupplier and repeatFromStream methods lies on its argument and on what is passed to the Stream.generate method.

While the repeatFromSupplier method receives an argument of type Supplier<Stream<T>> and passes this argument directly to the Stream.generate method, the repeatFromStream method receives an argument of type Stream<T> and creates a Supplier<Stream<T>> by means of the () -> stream inline lambda expression, which is immediately passed to the Stream.generate method.

I expected both methods to show the same behavior, as I thought the differences were only cosmetic.

However, this code shows different behaviors for the repeatFromSupplier and repeatFromStream methods. If you run it, you'll notice that the repeatFromSupplier method works as expected (it prints 1231231231), while the repeatFromStream method prints 123 and throws an IllegalStateException: stream has already been operated upon or closed.

Now, I do know why and when this exception is thrown. I'm not asking about it. Instead, I'd like to know why both methods behave differently.

Upvotes: 4

Views: 282

Answers (1)

Jorn Vernee
Jorn Vernee

Reputation: 33875

In the nominal case of s1 you call a supplier to create a new stream every time:

() -> Stream.of(1, 2, 3)

But in the case of s2, you create a single stream, and then always return that same instance from the supplier:

() -> stream

This is essentially the same as this code:

Stream<Integer> s = Stream.of(1, 2, 3); // now Stream.of is only executed once
Stream<Integer> s1 = repeatFromSupplier(() -> s);
s1.sequential().limit(10).forEachOrdered(System.out::print);

Which also throws an exception.

Upvotes: 4

Related Questions