Some of the developers are sometimes getting confuse stream with collections. Stream is not collection and does not contain any data.
Stream is indeed a fancy iterator which just takes the data from a source and process that only once. Stream does not hold any data but processes the elements one by one. Just like fluid is being passed through a pipe.
Let’s just try to prove this with a code Example.
In our previous blog, we used a Book bean class having properties like name, author, genres, and rating. Let’s continue with the same Example. Create a Book Bean Class first.
public class Book { private String name; private String Author; private String genre; private double rating; // a constructor public Book(String name, String Author, String genre, double rating) { this.name = name; this.Author = Author; this.genre = genre; this.rating = rating; } // and getters and setters for these 4 fields public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return Author; } public void setAuthor(String author) { Author = author; } public String getGenre() { return genre; } public void setGenre(String genre) { this.genre = genre; } public double getRating() { return rating; } public void setRating(double rating) { this.rating = rating; } @Override public String toString() { return "Book[ name=" + name + ", Author=" + Author + ", genre=" + genre + ", rating=" + rating + "]"; } }
Let’s do walk through this code to prove the point; Stream is not containers but just a one time flow of elements.
import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class StreamNotContainers { public static void main(String [] args) { // Step01 : Let's take a List of Books List < Book > books = new ArrayList < > (); // Step02 : Add Some books here to operate upon books.add(new Book("The Alchemist", "Paulo Coelho", "Adventure", 4.40)); books.add(new Book("The Notebook", "Nicholas Sparks", "Romance", 4.10)); books.add(new Book("Horror Cocktail", "Robert Bloch", "Horror", 2.67)); books.add(new Book("House of Leaves", "Mark Z. Danielewski", "Horror", 4.10)); // Step:03 Let's now filter the books by genres and Rating > 3 // Here we are operating on books stream List <Book> popularHorrorBookss = books.stream() .filter((book) -> book.getGenre().equalsIgnoreCase("Horror")) .filter((book) -> book.getRating() > 3) .collect(Collectors.toList()); // Step:04 Let's Print these books popularHorrorBookss.forEach(book -> System.out.println(book)); // Step:05 Let's Now again anoter stream to find only Romantic books // Perfectly fine untill now List < Book > popularRomanticBookss = books.stream() .filter((book) -> book.getGenre().equalsIgnoreCase("Romance")) .filter((book) -> book.getRating() > 3) .collect(Collectors.toList()); popularRomanticBookss.forEach(book -> System.out.println(book)); // Step:06 let's check if we can find first Horror and then Romantics books from creating only one stream Stream<Book> stream = books.stream(); stream.filter((book) -> book.getGenre().equalsIgnoreCase("Horror")) .filter((book) -> book.getRating() > 3) .collect(Collectors.toList()); //Step 07 refer the same Stream again to filter stream.filter((book) -> book.getGenre().equalsIgnoreCase("Romance")) .filter((book) -> book.getRating() > 3) .collect(Collectors.toList()); // Look What we get. } }
NOTE: First, Comment step 06,07 from the code and run.
In Step 01 we are having a List of Books, In Step 02 we have added some books to the books object of a list.
Step 03 we have created a Stream out of books List collection and filtered that
First with genres and then ratings, finally collecting them back to another list object popularHorrorBookss and printing them using for each of List.
Same way in Step o5 we are filtering the books rating > 3 and genres= Romance.
Until now everything is fine.
Now, Let’s try to use the same stream for filtering 2 times first Hoor books and then Romantics books.
Step 06 let’s create s Stream first from the books and store the reference to books. using books let’s first filter the Horror Books.
In Step 07 let’s use the same reference and filter the Romantics books.
See What Happens, here comes the Exception
Book [name=The Notebook, Author=Nicholas Sparks, genre=Romance, rating=4.1] Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed at java.base/java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203) at java.base/java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94) at java.base/java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:629) at java.base/java.util.stream.ReferencePipeline$2.<init>(ReferencePipeline.java:165) at java.base/java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:164) at StreamNotContainers.main(StreamNotContainers.java:48)
As the Exception itself suggest – java.lang.IllegalStateException: stream has already been operated upon or closed
Streams are like the pipeline, Through which data will flow only once and after that stream will be disposed of. Stream can’t be used again.
This proves our point that Streams are not data containers but a fancy iterator or a pipeline.