Stream API

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

TL;DR

  • Stream API (Java 8) is a declarative, functional approach to processing collections of data.
  • Instead of writing imperative for loops (how to do it), you declare a pipeline of operations (what you want).
  • Streams are Lazy. Intermediate operations (filter, map) don’t actually execute until a Terminal operation (collect, count) is invoked.

Concept

Before Java 8, if you wanted to find the names of all employees who earn over $100k, sort them alphabetically, and put them in a list, you had to write a massive, 15-line for loop with if statements and a custom Collections.sort() comparator.

Streams allow you to do this in a single, highly readable pipeline. You pipe the collection through a filter (removes low earners), then a map (extracts the names), then sorted (alphabetizes), and finally collect (packs them into a new List).

The beauty of Streams is laziness. If your terminal operation is .findFirst(), the stream will process elements one by one through the filter/map chain and instantly stop processing as soon as it finds a single match, saving massive CPU cycles.

Examples

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

class Employee {
    String name;
    int salary;
    // constructors/getters
}

public class StreamDemo {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("Alice", 120000),
            new Employee("Bob", 80000),
            new Employee("Charlie", 150000)
        );

        // Declarative, Functional Pipeline
        List<String> highEarners = employees.stream()
            .filter(e -> e.getSalary() > 100000) // Keep > 100k
            .map(Employee::getName)              // Extract just the name
            .sorted()                            // Sort alphabetically
            .collect(Collectors.toList());       // Pack into a new List

        System.out.println(highEarners); // Prints: [Alice, Charlie]
    }
}

Interview Questions

Q: What is the difference between an Intermediate and a Terminal operation?
A: - Intermediate Operations (filter, map, sorted, limit): They always return another Stream. They are lazy; they do absolutely nothing when called. They just build up an execution plan.

  • Terminal Operations (collect, count, forEach, findAny): They return a concrete result (a List, an int, a boolean) or void. Invoking a terminal operation is what actually ignites the pipeline and causes data to flow through it. A stream can only have exactly one terminal operation at the very end.

Q: Can a Stream be reused?
A: No. Streams are strictly “single-use”. Once a terminal operation has been executed on a stream, the stream is considered closed and consumed. If you try to call another operation on that exact same stream instance, Java will throw an IllegalStateException: stream has already been operated upon or closed. You must generate a brand new stream from the source collection.