In this lesson, we will explore some of the key differences between the Streams and Collections in Java.
Streams VS Collections
Streams | Collections | |
---|---|---|
Can not add or modify elements. It’s a fixed dataset | Can add or modify elements whenever we want | |
Elements in a stream can be accessed only in sequence | Elements in a collection can be accessed in any order | |
Streams are traversable only once. | Collections can be traversed multiple times. | |
Streams are lazily constructed. | Collections are eagerly constructed. | |
Examples
Example 1:
We can add or remove elements from collections, but we can’t do that from streams.
class Test { public static void main(String[] args) { List<String> list = new ArrayList<>(); // Collections // add items list.add("item1"); list.add("item2"); // remove items list.remove("item2"); // Streams list.stream() ... // we don't have any methods for adding or removing the elements from the stream } }
Example 2:
We can iterate over elements of the collections as many times as we want, but we can iterate only once over the elements of the stream.
class Test { public static void main(String[] args) { List<String> names = new ArrayList<>(Arrays.asList("Steve", "Melissa", "Megan", "Joshua", "Alexander", "Jim", "Paul")); // iterate over collection - 1. for (String name : names) { System.out.print(name + " "); } System.out.println(); // iterate over collection - 2. for (String name : names) { System.out.print(name + " "); } System.out.println(); Stream<String> namesStream = names.stream(); //iterate over a stream - 1. namesStream.forEach(name -> System.out.print(name + " ")); System.out.println(); //iterate over a stream - 2. namesStream.forEach(name -> System.out.println(name + " ")); } }
Output :
Steve Melissa Megan Joshua Alexander Jim Paul Steve Melissa Megan Joshua Alexander Jim Paul Steve Melissa Megan Joshua Alexander Jim Paul Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed at java.base/java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279) at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658) at com.company.Test.main(User.java:36)
You see that we got an exception when we tried to iterate over a stream for the second time. The stream has been closed after the first iteration, so that we can use it only once.
Example 3:
All the elements get computed initially in collections, so we can say that the collections are eagerly constructed.
Streams are lazily constructed i.e intermediate operations are not evaluated until the terminal operation is invoked.
class Test { public static void main(String[] args) { List<String> names = new ArrayList<>(Arrays.asList("Steve", "Melissa", "Megan", "Joshua", "Alexander", "Jim", "Paul")); names.stream() .filter(name -> name.length() > 5) .limit(2) .forEach(System.out::println); // here, the strings are evaluated until the 2 strings with length > 5 are found. } }
I hope this tutorial was helpful to you. To learn more, check out other Java Functional Programming tutorials.