Java 8 Streams Explained: From Loops to Functional Thinking
Imperative vs Declarative
If you’re new to Streams or come from a DSA background, you’re probably used to the imperative style of coding — where you control every move, every loop, every variable.
But Streams are more declarative.
Instead of telling the system how to do something step by step, you describe what you want:
“From the list of people, give me the names of those older than 30.”
That’s exactly how Stream code looks.
What is a Stream?
A Stream in Java is a wrapper over a data source (like a List, Set, or an array).
It lets you apply functional-style operations (like filter, map, collect) in a pipeline.
Streams do not modify the original source.
They process elements lazily — i.e., operations are only executed when a terminal operation (like collect) is called.
Example – Get the Names of People Older Than 30
Let’s say you have a List<Person> and want to extract the names of people older than 30.
List<String> namesOfAdults = people // Our data source: a list of Person objects .stream() // Convert the list into a Stream .filter(p -> p.getAge() > 30) // Intermediate operation: filter based on age .map(Person::getName) // Intermediate operation: transform Person to name .collect(Collectors.toList()); // Terminal operation: collect the results into a listHow Streams Work (Under the Hood)Streams don’t execute step-by-step like a loop.Instead, they build a pipeline of operations: Intermediate operations like filter and map are just recorded. When a terminal operation like collect() is called, it executes the entire pipeline in one efficient pass.That’s why it feels different — it’s a new mental model for writing expressive and efficient code.