Hi Everyone reading,
In the previous article, we explored Intermediate Operations in Java Streams — how they transform and prepare data.
Now it’s time to understand the final and most important step of stream processing:
Without a terminal operation, a stream does nothing.
Let’s understand why.
What are Terminal Operations?
Terminal operations:
- Produce a result.
- Trigger execution of intermediate operations.
- Close the stream after execution.
- Cannot be reused once executed.
Example
List<Integer> list = List.of(1,2,3,4,5);
list.stream()
.filter(n -> n % 2 == 0)
.forEach(System.out::println); // Terminal operation
Here:
filter() → Intermediate
forEach() → Terminal (executes the stream)
Why Terminal Operations Are Important
Streams are lazy.
Intermediate operations build a pipeline, but execution starts only when a terminal operation is called.
No terminal operation → No processing.
Types of Terminal Operations
Terminal operations can be grouped into:
- Iteration operations
- Reduction operations
- Collection operations
- Matching operations
- Finding operations
Let’s explore each.
1 forEach()
Used to iterate over elements.
List<String> names = List.of("Ram", "Amit", "Shyam");
names.stream()
.forEach(System.out::println);
Output
Ram
Amit
Shyam
Note: Prefer forEachOrdered() when working with parallel streams and order matters.
2 collect()
Most powerful terminal operation.
Used to collect stream elements into:
- List
- Set
- Map
- String
- Custom collections
Collect to List
List<Integer> evenNumbers =
List.of(1,2,3,4,5).stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList()); // [2,4]
Collect to Map
Map<String, Integer> map =
List.of("Ram", "Amit", "Shyam").stream()
.collect(Collectors.toMap(
name -> name,
name -> name.length()
)); // {Ram=3, Amit=4, Shyam=5}
3 reduce()
Used to combine elements into a single result.
Example: Sum of numbers
int sum = List.of(1,2,3,4)
.stream()
.reduce(0, (a,b) -> a + b);
System.out.println(sum);
Output
10
Used for:
- Sum
- Product
- Custom aggregation
4 count()
Returns number of elements.
long count = List.of(1,2,3,4,5)
.stream()
.filter(n -> n > 2)
.count();
System.out.println(count);
Output
3
In the above example, we only count the numbers that are greater than 2.
5 anyMatch(), allMatch(), noneMatch()
Used for condition checking.
boolean anyEven =
List.of(1,3,5,6).stream()
.anyMatch(n -> n % 2 == 0); // true
boolean allPositive =
List.of(1,2,3).stream()
.allMatch(n -> n > 0); // true
boolean result = List.of(5, 10, 15, 20).stream()
.noneMatch(n -> n < 0); // true
These return boolean.
6 findFirst() and findAny()
Return an Optional.
Optional<Integer> first =
List.of(10,20,30)
.stream()
.findFirst(); // Optional[10]
findAny() is useful with parallel streams.
Optional<Integer> result =
List.of(10, 20, 30, 40, 50)
.parallelStream()
.findAny(); // Optional[30]
Note: In the output of findAny(), there could be any element within the provided list in the output. In this case it was Optional[30], but 30 could be 10,20,40 or 50. findAny() returns any one element in the given list.
7 min() and max()
Find minimum or maximum element.
Optional<Integer> max =
List.of(1,5,2,9,3)
.stream()
.max(Integer::compareTo); // 9
Optional<Integer> min =
List.of(1,5,2,9,3)
.stream()
.min(Integer::compareTo); // 1
Important Characteristics of Terminal Operations
| Feature | Description |
|---|---|
| Triggers Execution | Executes all intermediate operations |
| Produces Result | Returns value or side effect |
| Closes Stream | Stream cannot be reused |
| Non-lazy | Actually performs computation |
Stream Cannot Be Reused
Stream<Integer> stream = Stream.of(1,2,3);
stream.forEach(System.out::println);
// This will throw IllegalStateException
stream.count();
Once a terminal operation is called, the stream is closed.
Real-World Example (Employee Processing)
Problem statement: For a given list of employees, find the average salary.
class Employee {
String name;
int salary;
Employee(String name, int salary) {
this.name = name;
this.salary = salary;
}
public String toString() {
return name + " : " + salary;
}
}
public class Main {
public static void main(String[] args) {
List<Employee> employees = List.of(
new Employee("Ram", 50000),
new Employee("Amit", 70000),
new Employee("Shyam", 40000),
new Employee("Ankit", 90000)
);
// Get average salary
double avgSalary = employees.stream()
.mapToInt(e -> e.salary)
.average()
.orElse(0);
System.out.println("Average Salary: " + avgSalary);
}
}
Output
Average Salary: 62500.0
Intermediate vs Terminal Operations
| Intermediate | Terminal |
|---|---|
| Returns Stream | Returns result |
| Lazy | Executes pipeline |
| Can chain | Ends stream |
filter(), map()
|
collect(), reduce(), count()
|
Conclusion
Terminal operations are the final step in stream processing.
They:
- Execute the pipeline
- Produce meaningful results
- Close the stream
Mastering terminal operations like collect(), reduce(), count(), and findFirst() makes you confident with Java Streams in real-world applications.
What’s Next?
In the next article, we will explore:
Collectors in depth
Top comments (0)