Reputation: 34460
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
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