Terminal Operations

Basic Java
≈ 2 min read

A terminal operation starts the pipeline

As long as a chain contains only intermediate operations, it merely describes future work. A terminal operation starts the processing and finishes the stream. After that, the same stream cannot be reused.

Get a collection, a count, or perform an action

toList() collects elements into a list, count() returns their number, and forEach() performs an action for the elements.

List<String> names = List.of("Ann", "Bob", "Alexander");

long count = names.stream()
        .filter(name -> name.length() > 3)
        .count();

The value of count is 1. If a list is needed instead, the same chain can end with toList().

Checks and searches can finish early

anyMatch, allMatch, and noneMatch check elements against a condition. findFirst and findAny search for an element. These operations do not have to traverse the entire stream when the answer is already known.

boolean hasLongName = names.stream()
        .anyMatch(name -> name.length() > 8);

As soon as a matching string is found, further traversal may be unnecessary. A search result is returned through Optional because a matching element may not exist. We will examine Optional itself later in this section.

min, max, and reduce fold elements

min and max find an extreme element according to a Comparator. reduce repeatedly combines elements into one result.

int sum = Stream.of(3, 5, 7)
        .reduce(0, Integer::sum);

The initial value here is 0, and each next element is added to the accumulated result. For numeric sums, we will later see more direct operations on primitive streams, but reduce shows the general idea of reduction.

A terminal operation finishes the stream. If the same source data needs another kind of processing afterward, create a new stream from the source instead of trying to reuse the old one.