Stream Pipeline

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • A Stream Pipeline is the sequence of operations performed on a stream.
  • It consists of three parts: Source -> Intermediate Operation(s) -> Terminal Operation.
  • Data flows through the pipeline lazily; nothing happens until the terminal operation is invoked.

Concept

Think of a Stream Pipeline like an assembly line in a factory.

  1. Source: The raw materials entering the factory (e.g., a List).
  2. Intermediate Operations: The workstations. Each workstation takes a part, modifies or filters it, and passes it to the next station (e.g., filter(), map()).
  3. Terminal Operation: The packaging station at the end that produces the final product (e.g., collect(), count()).

The beauty of the pipeline is that it is evaluated vertically, not horizontally.

Examples

import java.util.Arrays;
import java.util.List;

public class PipelineExample {
    public static void main(String[] args) {
        
        List<String> names = Arrays.asList("Zack", "Alice", "Bob", "Aaron");
        
        // The Pipeline
        names.stream()                           // 1. Source
            .filter(n -> {                       // 2. Intermediate
                System.out.println("Filter: " + n);
                return n.startsWith("A");
            })
            .map(n -> {                          // 3. Intermediate
                System.out.println("Map: " + n);
                return n.toUpperCase();
            })
            .forEach(System.out::println);       // 4. Terminal
            
        // Expected Output Execution Order:
        // Filter: Zack
        // Filter: Alice
        // Map: Alice
        // ALICE
        // Filter: Bob
        // Filter: Aaron
        // Map: Aaron
        // AARON
    }
}

Interview Questions

Q: How does the Stream Pipeline optimize execution?
A: The pipeline uses Lazy Evaluation and Short-Circuiting. In the example above, Java doesn’t filter the entire list, store the results, and then map the entire list. It takes the first item (“Zack”), passes it through filter, fails, and drops it. It takes “Alice”, passes filter, passes map, and prints it. If we had used .findFirst() instead of .forEach(), the pipeline would have stopped immediately after printing “ALICE”, completely ignoring “Bob” and “Aaron”.

Q: Can you reuse a Stream Pipeline?
A: No. Once the terminal operation completes, the stream is considered “consumed” or “closed”. If you try to invoke another terminal operation on the same stream reference, you will get an IllegalStateException. You must build a new pipeline from the source.