Stream API
The Stream API (Java 8+) enables functional-style processing of collections and arrays. It allows you to transform, filter, aggregate, and process data declaratively with high readability and optional parallelism.
This is a very high-frequency interview topic and heavily used in modern Java applications.
What Is a Stream?
A stream is:
- A sequence of elements
- From a data source (Collection, Array, I/O)
- Supporting functional operations
- Not a data structure
- Does not modify the source
list.stream();
Why Stream API Was Introduced
- Reduce boilerplate code
- Enable functional programming
- Improve readability and maintainability
- Support easy parallelism
- Separate what from how
Stream Pipeline (Core Concept)
A stream operation follows this pipeline:
Source → Intermediate Operations → Terminal Operation
Example:
list.stream()
.filter(x -> x > 10)
.map(x -> x * 2)
.forEach(System.out::println);
Characteristics of Streams
- No storage – operates on source
- Lazy evaluation – executes only on terminal operation
- One-time use – cannot be reused
- Functional – operations do not mutate source
Creating Streams
From Collection
Streams = list.stream();
From Array
Streams = Arrays.stream(arr);
Using Stream.of()
Streams = Stream.of(1, 2, 3);
Intermediate Operations (Lazy)
Return a new Stream.
Common Intermediate Operations
| Method | Purpose |
|---|---|
| filter() | Condition-based filtering |
| map() | Transform elements |
| flatMap() | Flatten nested streams |
| distinct() | Remove duplicates |
| sorted() | Sort elements |
| limit() | Limit size |
| skip() | Skip elements |
| peek() | Debugging |
filter()
list.stream()
.filter(x -> x % 2 == 0)
.forEach(System.out::println);
map()
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
flatMap() (Interview Favorite)
List> data = List.of(List.of(1,2), List.of(3,4)); data.stream() .flatMap(List::stream) .forEach(System.out::println);
Converts nested structure into flat stream.
Terminal Operations (Trigger Execution)
Consume the stream and produce a result or side effect.
Common Terminal Operations
| Method | Result |
|---|---|
| forEach() | Iteration |
| collect() | Collection |
| reduce() | Single value |
| count() | Long |
| anyMatch() | boolean |
| allMatch() | boolean |
| noneMatch() | boolean |
| findFirst() | Optional |
| findAny() | Optional |
collect()
Listeven = list.stream() .filter(x -> x % 2 == 0) .collect(Collectors.toList());
reduce() (Interview Favorite)
int sum =
list.stream()
.reduce(0, Integer::sum);
Stream vs Collection (Interview Table)
| Aspect | Stream | Collection |
|---|---|---|
| Storage | ❌ No | ✔ Yes |
| Iteration | Internal | External |
| Reusability | ❌ No | ✔ Yes |
| Laziness | ✔ Yes | ❌ No |
| Parallelism | ✔ Easy | ❌ Manual |
Sequential vs Parallel Streams
Sequential (Default)
list.stream();
Parallel Stream
list.parallelStream();
- Automatic multi-threading
- Not always faster
- Order may not be preserved
Stateless vs Stateful Operations
- Stateless: map, filter (preferred)
- Stateful: distinct, sorted (costly)
Optional with Streams
Optionalmax = list.stream().max(Integer::compareTo);
Avoids NullPointerException.
Common Beginner Mistakes
- Modifying source inside stream
- Reusing a stream
- Using forEach() instead of map()
- Overusing parallel streams
- Writing complex logic in lambdas
Best Practices (Production-Grade)
- Keep lambdas small and readable
- Prefer method references
- Use streams for data transformation, not business logic
- Be cautious with parallel streams
- Prefer Collectors for aggregation
Interview-Ready Answers
Short Answer
Stream API provides a functional way to process collections in Java.
Detailed Answer
In Java, the Stream API enables functional-style operations on sequences of elements. Streams support lazy evaluation, internal iteration, and can be processed sequentially or in parallel using intermediate and terminal operations without modifying the underlying data source.
Key Takeaway
Streams are about data processing, not data storage. They make Java code cleaner, declarative, and scalable, especially when combined with lambdas and functional interfaces.
Stream API Examples (1-25)
1. Creating a Stream from a Collection
import java.util.*;
class Demo {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
list.stream().forEach(System.out::println);
}
}
2. filter() – Select Elements
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3, 4)
.stream()
.filter(x -> x % 2 == 0)
.forEach(System.out::println);
}
}
3. map() – Transform Elements
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList("a", "bb", "ccc")
.stream()
.map(String::length)
.forEach(System.out::println);
}
}
4. Chaining filter() + map()
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3, 4)
.stream()
.filter(x -> x > 2)
.map(x -> x * 10)
.forEach(System.out::println);
}
}
5. sorted() – Natural Order
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(3, 1, 2)
.stream()
.sorted()
.forEach(System.out::println);
}
}
6. sorted() with Comparator (Descending)
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(3, 1, 2)
.stream()
.sorted((a, b) -> b - a)
.forEach(System.out::println);
}
}
7. distinct() – Remove Duplicates
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 1, 2, 3, 3)
.stream()
.distinct()
.forEach(System.out::println);
}
}
8. limit() and skip()
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3, 4, 5)
.stream()
.skip(2)
.limit(2)
.forEach(System.out::println);
}
}
9. count() – Terminal Operation
import java.util.*;
class Demo {
public static void main(String[] args) {
long c = Arrays.asList("a", "bb", "ccc")
.stream()
.filter(s -> s.length() > 1)
.count();
System.out.println(c);
}
}
10. findFirst() and findAny()
import java.util.*;
class Demo {
public static void main(String[] args) {
System.out.println(
Arrays.asList(1, 2, 3).stream().findFirst().get()
);
}
}
11. anyMatch(), allMatch(), noneMatch()
import java.util.*;
class Demo {
public static void main(String[] args) {
boolean anyEven =
Arrays.asList(1, 3, 4).stream().anyMatch(x -> x % 2 == 0);
System.out.println(anyEven);
}
}
12. reduce() – Aggregate Values
import java.util.*;
class Demo {
public static void main(String[] args) {
int sum = Arrays.asList(1, 2, 3)
.stream()
.reduce(0, Integer::sum);
System.out.println(sum);
}
}
13. collect() to List
import java.util.*;
import java.util.stream.*;
class Demo {
public static void main(String[] args) {
List<Integer> evens =
Arrays.asList(1, 2, 3, 4)
.stream()
.filter(x -> x % 2 == 0)
.collect(Collectors.toList());
System.out.println(evens);
}
}
14. collect() to Set
import java.util.*;
import java.util.stream.*;
class Demo {
public static void main(String[] args) {
Set<Integer> set =
Arrays.asList(1, 1, 2)
.stream()
.collect(Collectors.toSet());
System.out.println(set);
}
}
15. Collectors.groupingBy()
import java.util.*;
import java.util.stream.*;
class Demo {
public static void main(String[] args) {
Map<Integer, List<String>> map =
Arrays.asList("a", "bb", "ccc")
.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println(map);
}
}
16. Collectors.partitioningBy()
import java.util.*;
import java.util.stream.*;
class Demo {
public static void main(String[] args) {
Map<Boolean, List<Integer>> parts =
Arrays.asList(1, 2, 3, 4)
.stream()
.collect(Collectors.partitioningBy(x -> x % 2 == 0));
System.out.println(parts);
}
}
17. Collectors.joining()
import java.util.*;
import java.util.stream.*;
class Demo {
public static void main(String[] args) {
String s =
Arrays.asList("Java", "Stream", "API")
.stream()
.collect(Collectors.joining(" "));
System.out.println(s);
}
}
18. Primitive Streams (IntStream)
import java.util.stream.*;
class Demo {
public static void main(String[] args) {
int sum = IntStream.range(1, 5).sum();
System.out.println(sum);
}
}
19. mapToInt() – Avoid Boxing
import java.util.*;
class Demo {
public static void main(String[] args) {
int total =
Arrays.asList("a", "bb", "ccc")
.stream()
.mapToInt(String::length)
.sum();
System.out.println(total);
}
}
20. Parallel Stream
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3, 4)
.parallelStream()
.forEach(System.out::println);
}
}
21. Sequential vs Parallel (When Order Matters)
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3)
.parallelStream()
.forEachOrdered(System.out::println);
}
}
22. Laziness of Streams (Interview Trap)
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3)
.stream()
.filter(x -> {
System.out.println("filter " + x);
return x > 1;
});
// ❌ No terminal operation → nothing executes
}
}
23. Stream Can Be Consumed Only Once
import java.util.*;
class Demo {
public static void main(String[] args) {
var s = Arrays.asList(1, 2).stream();
s.forEach(System.out::println);
// s.forEach(System.out::println); // ❌ IllegalStateException
}
}
24. peek() for Debugging
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3)
.stream()
.peek(System.out::println)
.map(x -> x * 2)
.forEach(System.out::println);
}
}
25. Interview Summary – Stream API
source → intermediate ops → terminal op
Key Points
- Streams are lazy
- Intermediate ops return a new stream
- Terminal ops trigger execution
- Prefer primitive streams for performance
- Parallel streams need care with ordering & shared state