Pipeline Anatomy

Streams · lesson 3 of 42 · 3 min read

The three parts of every pipeline, and why nothing runs without a terminal op.

Open this lesson in the learning hub

Key points

  • Three parts: a source, zero or more intermediate ops, and exactly one terminal op.
  • Intermediate ops return a Stream. filter, map, sorted, limit only record your intent.
  • Terminal ops return something that is not a Stream. toList, count, forEach, reduce fire the whole chain.
  • No terminal op means no work at all. This is the number one stream surprise.
  • After the terminal op the stream is spent. Build a fresh one from the source.

Example

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<String> words = List.of("stream", "lazy", "terminal", "op");

        Stream<String> pipeline = words.stream()      // 1. source
                .filter(w -> w.length() > 2)          // 2. intermediate
                .map(String::toUpperCase);            // 3. still intermediate

        System.out.println("Built the pipeline. Nothing has run.");

        List<String> result = pipeline.toList();      // 4. terminal -> now it runs
        System.out.println("toList   : " + result);

        // Terminal ops come in three flavours: a value, a container, or nothing.
        System.out.println("count    : " + words.stream().filter(w -> w.contains("a")).count());
        System.out.println("anyMatch : " + words.stream().anyMatch(w -> w.length() == 2));
        words.stream().limit(2).forEach(w -> System.out.println("forEach  : " + w));
    }
}

Nothing happens until the terminal op asks for a result.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Streams course, and every lesson in it is listed on the Streams contents page.