Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this java Stream operated upon twice?

Tags:

The Java 8 API says:

Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.

So why the following code throws :

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

Stream<Integer> stream = Stream.of(1,2,3); stream.filter( x-> x>1 ); stream.filter( x-> x>2 ).forEach(System.out::print); 

The first filtering operation according to the API is not supposed to operate on the Stream.

like image 703
Ilias Stavrakis Avatar asked Jan 08 '16 12:01

Ilias Stavrakis


People also ask

Can Java stream be reused and visited many times?

No. Java streams, once consumed, can not be reused by default. As Java docs say clearly, “A stream should be operated on (invoking an intermediate or terminal stream operation) only once.

How do you fix stream has already been operated upon or closed?

The Solution. Simply put, the solution consists of creating a new Stream each time we need one. We've defined the streamSupplier object with the type Stream<String>, which is exactly the same type which the #get() method returns.

What is double stream in Java?

A sequence of primitive double-valued elements supporting sequential and parallel aggregate operations. This is the double primitive specialization of Stream .

Can we use stream twice?

From the documentation: A stream should be operated on (invoking an intermediate or terminal stream operation) only once. A stream implementation may throw IllegalStateException if it detects that the stream is being reused. So the answer is no, streams are not meant to be reused.


2 Answers

This happens because you are ignoring the return value of filter.

Stream<Integer> stream = Stream.of(1,2,3); stream.filter( x-> x>1 ); // <-- ignoring the return value here stream.filter( x-> x>2 ).forEach(System.out::print); 

Stream.filter returns a new Stream consisting of the elements of this stream that match the given predicate. But it's important to note that it's a new Stream. The old one has been operated upon when the filter was added to it. But the new one wasn't.

Quoting from Stream Javadoc:

A stream should be operated on (invoking an intermediate or terminal stream operation) only once.

In this case, filter is the intermediate operation that operated on the old Stream instance.

So this code will work fine:

Stream<Integer> stream = Stream.of(1,2,3); stream = stream.filter( x-> x>1 ); // <-- NOT ignoring the return value here stream.filter( x-> x>2 ).forEach(System.out::print); 

As noted by Brian Goetz, you would commonly chain those calls together:

Stream.of(1,2,3).filter( x-> x>1 )                 .filter( x-> x>2 )                 .forEach(System.out::print); 
like image 86
Tunaki Avatar answered Oct 04 '22 22:10

Tunaki


filter() method uses the stream and returns one other Stream instance that you ignore in you example.

filter is a intermediate operation but you can't call filter twice on same stream instance

Your code should be write as follow:

Stream<Integer> stream = Stream.of(1,2,3);                                .filter( x-> x>1 )                                .filter( x-> x>2); stream.forEach(System.out::print); 

As filter is an intermediate operation, "nothing" is done when calling theses methods. All the job is really processed when calling forEach() method

like image 27
Prim Avatar answered Oct 04 '22 20:10

Prim