Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Part 4: Streams, Lambdas & Functional Java
JAVA

Part 4: Streams, Lambdas & Functional Java

Learn functional interfaces, stream pipelines, lambda expressions, method references, and Optional in Java.

1. Lambdas & Functional Interfaces

  • Functional interface: exactly one abstract method. Annotate with @FunctionalInterface (optional but documents intent).
  • Lambda: (params) -> expression|block — syntactic sugar for a single-method anonymous class.
  • Common ones: Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T>, BiFunction, UnaryOperator, BinaryOperator.
Predicate<String> p = s -> s.length() > 3;     // test(T) → boolean
Consumer<String> c = System.out::println;      // accept(T) → void
Supplier<Double> sp = Math::random;            // get() → T
Function<String, Integer> f = String::length;  // apply(T) → R
  • Capturing variables must be effectively final — a variable mutated after capture fails to compile.
  • Method reference :: is shorthand: ClassName::staticMethod, instance::method, ClassName::new (constructor).

2. The Streams Pipeline

  • Source → intermediate ops → terminal op. Intermediate ops are lazy; the pipeline runs only when a terminal op is invoked.
  • Intermediate: filter, map, flatMap, distinct, sorted, limit, peek, skip.
  • Terminal: collect, forEach, count, anyMatch/allMatch/noneMatch, findFirst/findAny, reduce, min, max.
  • Streams are one-shot — consuming a stream twice throws IllegalStateException.
  • Streams do not store data — they’re a view over a source.
List<String> names = people.stream()
        .filter(p -> p.age() > 18)
        .map(Person::name)
        .sorted()
        .limit(5)
        .collect(Collectors.toList());

Short-circuiting: limit, findFirst, anyMatch stop processing early (use with infinite Stream.generate).

3. Intermediate vs Terminal — the interview split

OperationTypeLaziness
filter/map/sortedintermediatelazy
distinctintermediatelazy
peekintermediatelazy (debug)
forEach/collectterminaleager
findFirst/anyMatchterminalshort-circuit

Gotcha: peek is (unofficially) for debugging; relying on its side effects for output ordering is fragile.

4. Terminal collectors

  • toList(), toSet(), toMap(...), joining(", "), groupingBy(...), partitioningBy(...), counting, summingInt, maxBy.
  • Collectors.groupingBy(Person::dept)Map<String, List<Person>>.
  • Collectors.joining(", ") for strings.

5. Optional — the null handler

  • Optional.of(x) — error if x null.
  • Optional.ofNullable(x) — empty if null.
  • orElse(v) — always evaluates v; orElseGet(supplier) — lazy, only on empty.
  • map, filter, flatMap chain.
  • get() throws NoSuchElementException if empty — prefer orElse.

Gotcha: orElse evaluates the fallback eagerly, so orElseGet is preferred when the fallback is expensive.

6. Common pitfalls

  • Boxed loss of precision: IntStream.range(...) avoids boxed Integer.
  • Parallel streams — use only when the reduction is associative and stateless; shared mutable state breaks determinism.
  • .stream() on null collection → NPE — guard with Optional.ofNullable(x).stream().
  • Sorting stabilitysorted() is stable for equal elements (JDK impl).
  • reduce(identity, BinaryOperator) vs collect — reduce is functional; collect is mutable accumulation (faster for lists).
  • Shuffle not available in Stream API; use Collections.shuffle.

One-liner: streams + lambdas are the single highest-leverage “modern Java” interview topic — know the pipeline, the lazy vs terminal split, and the Optional rules cold.

My Private Notes

Notes are auto-saved locally to this device.