Memra

Stream basics: source → intermediate → terminal

Lazy pipelines, common operations, and the single-use rule.

Streams: declarative data processing

A stream is a lazy pipeline — you describe what to do, and nothing executes until a terminal operation is called. Every stream has three parts:

  1. Source — produces elements
  2. Intermediate operations — transform elements lazily (return a new stream)
  3. Terminal operation — triggers execution, produces a result or side-effect
List<String> names = List.of("Alice", "Bob", "Charlie", "Anna");

long count = names.stream()              // source
    .filter(s -> s.startsWith("A"))      // intermediate — lazy
    .map(String::toUpperCase)            // intermediate — lazy
    .count();                            // terminal — executes the pipeline
// count = 2

Creating streams:

Stream<String>  s1 = collection.stream();
Stream<String>  s2 = Stream.of("a", "b", "c");
IntStream       s3 = IntStream.range(0, 5);       // 0, 1, 2, 3, 4
IntStream       s4 = IntStream.rangeClosed(1, 5); // 1, 2, 3, 4, 5

Key intermediate operations:

| Op | What it does | |---|---| | filter(Predicate) | keeps elements that pass | | map(Function) | transforms each element | | flatMap(Function) | maps each to a stream, then flattens | | distinct() | removes duplicates (uses equals) | | sorted() / sorted(Comparator) | sorts elements | | limit(n) | keeps at most n elements | | skip(n) | discards the first n elements | | peek(Consumer) | side-effect for debugging without consuming the stream |

Laziness in practice: filter and map do not iterate the list; they register an operation to be applied when the terminal op fires. This enables short-circuiting: findFirst() stops after the first match, potentially reading only a few elements.

NORMAL ~/memra/learn/java-from-zero/stream-pipeline utf-8 LF