What actually changes when you move from a collection to a stream in Java?
A collection stores elements. A stream computes them. That single difference explains everything else: the interfaces, the laziness, the single use, and the fact that a stream is a description of work rather than a container of results.
A collection has a size you can ask for at any time, supports random access where the implementation allows it, and can be iterated as many times as you like because the data is already there. A stream has none of those things. It has no size, no get method, and it can only be traversed once, because the traversal is the computation.
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> names = List.of("Ada", "Grace", "Alan", "Katherine");
// The imperative version.
List<String> imperative = new ArrayList<>();
for (String name : names) {
if (name.length() > 3) {
imperative.add(name.toUpperCase());
}
}
System.out.println("imperative : " + imperative);
// The stream version.
List<String> functional = names.stream()
.filter(n -> n.length() > 3)
.map(String::toUpperCase)
.collect(Collectors.collect(java.util.stream.Collectors.toList()));
System.out.println("stream : " + functional);
// A stream does not change the source.
System.out.println("source : " + names);
}
}The output shows both versions producing the same result, and the source list unchanged in both cases. The difference is in how you read the code: the imperative version tells you the order of the steps and leaves you to work out what it is computing, while the stream version names each step.
One performance point that is often misstated. Streams are not automatically faster than loops. They are usually about the same for a simple in-memory transformation, and the stream version allocates a little more because of the pipeline objects and, if you collect, the result collection. The win is not speed — it is that a pipeline can short-circuit, can be parallelised with a single call, and reads as a description of intent.