Java Streams API¶
The Java Streams API (introduced in Java 8) provides a declarative, functional-style way to process collections
of data.
It allows you to perform complex data transformations without explicit loops, making code more readable, concise,
and expressive.
What is a Stream?¶
A Stream is:
- Not a data structure
- A pipeline of operations
- Used to process data from a source (Collection, Array, I/O, etc.)
A stream does not store data.
It processes data on demand.
Key Characteristics¶
- ❌ No data storage
- ✅ Lazy evaluation
- ❌ Cannot be reused
- ✅ Functional-style operations
- ✅ Can be sequential or parallel
Stream Pipeline¶
A stream pipeline has three parts:
Source → Intermediate Operations → Terminal Operation¶
list.stream()
.filter(n -> n > 10)
.map(n -> n * 2)
.forEach(System.out::println);
````
---
## Stream Sources
Streams can be created from:
### Collections
```java
list.stream();
list.parallelStream();
Arrays¶
Static Stream methods¶
Strings¶
Types of Streams¶
Object Stream¶
Primitive Streams¶
IntStreamLongStreamDoubleStream
Primitive streams exist to avoid boxing/unboxing overhead.
Intermediate Operations¶
Intermediate operations:
- Return a new stream
- Are lazy
- Do not execute until a terminal operation is called
Common Intermediate Operations¶
filter¶
map¶
distinct¶
sorted¶
limit / skip¶
map vs mapToInt / mapToObj¶
Primitive streams need conversion:
mapToObj() exists only on primitive streams.
Terminal Operations¶
Terminal operations:
- Trigger execution
- Consume the stream
- Produce a result or side effect
Common Terminal Operations¶
forEach¶
collect¶
reduce¶
count¶
findFirst / findAny¶
anyMatch / allMatch / noneMatch¶
Collectors¶
Collectors are utility methods for collecting stream results.
Common Collectors¶
Collectors.toList()
Collectors.toSet()
Collectors.toMap()
Collectors.joining(", ")
Collectors.groupingBy()
Collectors.partitioningBy()
Example:
Map<String, List<Employee>> byDept =
employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
Lazy Evaluation¶
Streams execute only when needed.
☝️ Nothing prints until a terminal operation is called.
Parallel Streams¶
Parallel streams split work across threads automatically.
When to use¶
- Large datasets
- CPU-bound tasks
- Stateless operations
When NOT to use¶
- I/O operations
- Small datasets
- Order-sensitive logic
Stream vs Collection¶
| Feature | Collection | Stream |
|---|---|---|
| Stores data | Yes | No |
| Can iterate multiple times | Yes | No |
| Lazy | No | Yes |
| Functional style | No | Yes |
Common Pitfalls¶
- Reusing a stream ❌
- Modifying source during stream execution ❌
- Using streams for simple loops unnecessarily ❌
When to Use Streams¶
✅ Data transformation ✅ Filtering, mapping, grouping ✅ Readability over control
❌ Complex control flow ❌ Heavy mutation logic
Summary¶
- Streams provide a clean, functional approach to data processing
- Separate what you want from how it’s done
- Lazy, expressive, and powerful
- Best used for transformation-heavy logic
One-line takeaway¶
Streams turn loops into readable data pipelines.