14.1 Lambda Syntax — Anonymous Behaviour
A lambda expression is an inline implementation of a functional interface's single abstract method (Lesson 8):
() -> 42 // no params, expression body
x -> x * x // one param, parentheses optional
(a, b) -> a + b // two params
(String s) -> { int n = s.length(); return n; } // block body needs return
Rules: parameter types are usually inferred; a lambda can capture local variables only if they are effectively final (never reassigned) — the compiler copies the value into the lambda, so mutation would create two divergent copies; unlike anonymous inner classes, this inside a lambda refers to the enclosing object, and no separate .class instance ceremony is needed.
14.2 Method References — Four Kinds
| Kind | Syntax | Equivalent lambda |
|---|---|---|
| Static | Integer::parseInt | s -> Integer.parseInt(s) |
| Instance method of a particular object | System.out::println | x -> System.out.println(x) |
| Instance method of an arbitrary object of a type | String::toUpperCase | s -> s.toUpperCase() |
| Constructor | ArrayList::new | () -> new ArrayList<>() |
14.3 The Core Functional Interfaces (java.util.function)
| Interface | Method | Shape | Typical use |
|---|---|---|---|
Predicate<T> | test | T → boolean | filter |
Function<T,R> | apply | T → R | map |
Consumer<T> | accept | T → void | forEach |
Supplier<T> | get | () → T | lazy creation |
BinaryOperator<T> | apply | (T,T) → T | reduce |
14.4 Stream Pipelines — Declarative Data Processing
A stream is a one-shot pipeline over a data source: source → intermediate ops (lazy) → terminal op (triggers everything).
| Intermediate (lazy, return Stream) | Terminal (eager, end the stream) |
|---|---|
| filter, map, mapToInt, flatMap | forEach, collect, reduce |
| sorted, distinct, limit, skip, peek | count, min, max, sum/average (primitive) |
| — | anyMatch/allMatch/noneMatch, findFirst |
Laziness: nothing runs until the terminal op; elements flow one at a time through the whole pipeline, enabling short-circuiting (limit, findFirst). A stream cannot be reused after its terminal op (IllegalStateException) and never mutates its source.
14.5 Worked Examples
List<String> names = List.of("Asha", "Bala", "Arun", "Charu", "Amit");
// 1) filter + map + collect
List<String> aNames = names.stream()
.filter(n -> n.startsWith("A"))
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList()); // [AMIT, ARUN, ASHA]
// 2) reduce: sum of squares of even numbers
int sumSq = IntStream.rangeClosed(1, 6)
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.sum(); // 4 + 16 + 36 = 56
// 3) reduce with identity + accumulator
int product = Stream.of(1, 2, 3, 4).reduce(1, (a, b) -> a * b); // 24
// 4) grouping and counting
Map<Integer, List<String>> byLen = names.stream()
.collect(Collectors.groupingBy(String::length));
Map<Boolean, Long> partition = names.stream()
.collect(Collectors.partitioningBy(n -> n.length() > 4, Collectors.counting()));
Collectors to memorize: toList, toSet, toMap(k, v), joining(", "), groupingBy (optionally with a downstream collector), partitioningBy, counting, averagingDouble, summingInt.
14.6 Stream vs Collection & Parallel Note
A collection stores data; a stream describes a computation over data — no storage, lazy, single-use, and side-effect-free by convention. list.parallelStream() splits work across the common ForkJoinPool — safe only when operations are stateless and non-interfering; never mutate shared state from inside a parallel pipeline (that reintroduces Lesson 11's race conditions).
🎯 Exam Focus
- What is a lambda expression? State its syntax forms and the "effectively final" capture rule with an example that fails to compile.
- Explain the four kinds of method references with one example each.
- Differentiate intermediate and terminal stream operations. Why are streams called lazy, and why can't a stream be reused?
- Write a stream pipeline that, given a list of integers, prints the sum of squares of the odd numbers.
- Given a list of Employee(name, dept, salary), use streams to (a) group employees by dept, (b) find the average salary per dept, (c) get the top-2 earners.
- Differentiate map() and flatMap(); Collection and Stream.