1. Overview
In this brief article, weβre going to discuss a common Exception that we may encounter when working with the Stream class in Java 8:
IllegalStateException: stream has already been operated upon or closed.
Weβll discover the scenarios when this exception occurs, and the possible ways of avoiding it, all along with practical examples.
2. The Cause
In Java 8, each Stream class represents a single-use sequence of data and supports several I/O operations.
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.
Whenever a terminal operation is called on a Stream object, the instance gets consumed and closed.
Therefore, weβre only allowed to perform a single operation that consumes a Stream, otherwise, weβll get an exception that states that the Stream has already been operated upon or closed.
Letβs see how this can be translated to a practical example:
Stream<String> stringStream = Stream.of("A", "B", "C", "D");
Optional<String> result1 = stringStream.findAny();
System.out.println(result1.get());
Optional<String> result2 = stringStream.findFirst();
As a result:
A
Exception in thread "main" java.lang.IllegalStateException:
stream has already been operated upon or closed
After the #findAny() method is invoked, the stringStream is closed, therefore, any further operation on the Stream will throw the IllegalStateException, and thatβs what happened after invoking the #findFirst() method.
3. The Solution
Simply put, the solution consists of creating a new Stream each time we need one.
We can, of course, do that manually, but thatβs where the Supplier functional interface becomes really handy:
Supplier<Stream<String>> streamSupplier
= () -> Stream.of("A", "B", "C", "D");
Optional<String> result1 = streamSupplier.get().findAny();
System.out.println(result1.get());
Optional<String> result2 = streamSupplier.get().findFirst();
System.out.println(result2.get());
As a result:
A
A
Weβve defined the streamSupplier object with the type Stream<String>, which is exactly the same type which the #get() method returns. The Supplier is based on a lambda expression that takes no input and returns a new Stream.
Invoking the functional method get() on the Supplier returns a freshly created Stream object, on which we can safely perform another Stream operation.
4. Conclusion
In this quick tutorial, weβve seen how to perform terminal operations on a Stream multiple times, while avoiding the famous IllegalStateException that is thrown when the Stream is already closed or operated upon.
The code backing this article is available on GitHub. Once you're
logged in as a Baeldung Pro Member, start learning and coding on the project.