DEV Community

Priyank Bhardwaj
Priyank Bhardwaj

Posted on

Terminal Operations in Java Streams

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
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Output

Ram
Amit
Shyam
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Output

10
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Output

3
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

These return boolean.

6 findFirst() and findAny()

Return an Optional.

Optional<Integer> first =
    List.of(10,20,30)
        .stream()
        .findFirst(); // Optional[10]
Enter fullscreen mode Exit fullscreen mode

findAny() is useful with parallel streams.

Optional<Integer> result =
                List.of(10, 20, 30, 40, 50)
                    .parallelStream()
                    .findAny(); // Optional[30]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Output

Average Salary: 62500.0
Enter fullscreen mode Exit fullscreen mode

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)