Streams and Lambda Expressions
Lambdas and functional interfaces
A lambda supplies the implementation of a functional interface - exactly one abstract method (default and static methods do not count). The target type comes from context: assignment, argument, or return. Method references come in four flavours: static (Integer::parseInt), bound instance (System.out::println), unbound instance (String::length), and constructor (ArrayList::new).
Pipeline rules
A stream has a source, zero or more lazy intermediate operations (filter, map, flatMap, distinct, sorted, peek, limit, skip) and one terminal operation (forEach, collect, count, reduce, anyMatch, findFirst, toList). Nothing executes until the terminal operation runs, and a stream cannot be reused (IllegalStateException). map is one-to-one; flatMap flattens a stream of streams.
Collectors and Optional
Collectors.toMap throws IllegalStateException on duplicate keys unless a merge function is supplied. groupingBy returns a Map<K, List<V>>; partitioningBy always returns exactly two keys (true and false); joining works on CharSequence elements. Optional.get() on an empty Optional throws NoSuchElementException - prefer orElse, orElseGet (lazy), or orElseThrow.
Traps
allMatch on an empty stream returns true, while anyMatch returns false. Primitive streams (IntStream.range(0, 10), upper bound exclusive) need boxed() before collecting to List<Integer>. Avoid stateful lambdas and shared mutable state in parallel streams; use forEachOrdered when encounter order matters.
Worked rule: if a pipeline produces no output at all, look for a missing terminal operation - intermediate operations alone do nothing.