๐ What Are Streams?
A Stream in Java is a sequence of data elements supporting sequential and parallel aggregate operations.
Simply put, Streams let you process data declaratively โ what to do, not how to do it.
They bring a functional programming flavor to Java.
You can think of a Stream as a pipeline of operations that transform data step by step.
Key points:
- Streams donโt store data โ they operate on existing data sources (like Collections, Arrays, or I/O channels).
- They are lazy โ computation happens only when a terminal operation is invoked.
- They are functional โ once used, a Stream cannot be reused.
๐ Collections vs Streams
Feature | Collections | Streams |
---|---|---|
Nature | Data structure (stores elements) | Data pipeline (processes elements) |
Iteration | External (you control iteration with loops) | Internal (handled by Stream API) |
Reusability | Can be reused | Consumed once |
Evaluation | Eager (stores all elements) | Lazy (computes on demand) |
Example | List<Integer> list = Arrays.asList(1,2,3); |
list.stream().filter(n -> n>1) |
๐ In short: Collections are about data storage, Streams are about data processing.
Lambda Notation Refresher
Before Streams, weโd typically write something like:
for (String name : names) {
if (name.startsWith("A")) {
System.out.println(name);
}
}
With Lambdas and Streams, it becomes:
names.stream()
.filter(name -> name.startsWith("A"))
.forEach(System.out::println);
See the difference?
No loops, no boilerplate โ just intent.
A lambda expression is essentially a short block of code that takes input and returns output, often used as an argument to Stream methods like filter()
, map()
, and forEach()
.
The Stream Pipeline
Every Stream operation flows through three main stages:
1. Source
Where data originates โ e.g., a collection, array, or file.
Stream<String> stream = List.of("Java", "Python", "C++").stream();
2. Intermediate Operations
Transform or filter data.
These are lazy โ they donโt do anything until a terminal operation is called.
Examples:
filter()
, map()
, sorted()
, distinct()
, limit()
stream.filter(lang -> lang.startsWith("J"))
.map(String::toUpperCase);
3. Terminal Operations
Trigger the actual processing and produce a result.
Examples:
forEach()
, collect()
, count()
, findFirst()
long count = List.of("Java", "Python", "C++")
.stream()
.filter(lang -> lang.startsWith("J"))
.count();
System.out.println(count); // Output: 1
Think of it as:
Source โ Intermediate Operations โ Terminal Operation
Simple Examples to Get Started
Example 1: Filtering and Printing
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
numbers.stream()
.filter(number -> number % 2 == 0)
.forEach(System.out::println);
// Output: 2 4 6
Example 2: Mapping to Another Type
List<String> words = List.of("java", "stream", "api");
List<String> upper = words.stream()
.map(String::toUpperCase)
.toList();
System.out.println(upper); // [JAVA, STREAM, API]
Example 3: Combining Operations
List<String> names = List.of("Ayush", "Raj", "Anita", "Vikram");
long count = names.stream()
.filter(name -> name.startsWith("A"))
.map(String::toUpperCase)
.count();
System.out.println(count); // 2
Coming Up Next
In upcoming parts, weโll deep dive into Intermediate Operations like:
filter()
, map()
, flatMap()
, distinct()
, sorted()
, and more with real-world use cases and performance insights.
But before we go deeper into Stream operations, we need to understand the functional interfaces that make all this possible.
We'll explore:
-
Predicate
,Function
,Consumer
,Supplier
, andBiFunction
- How they power Stream methods
- When and how to create your own functional interfaces
Stay tuned and follow to get notified when the next part drops!
๐ Next Up (Part 2): Functional Interfaces You Must Know
Top comments (0)