Streams API & Lambda Expressions
1019 words
5 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# Streams API & Lambda Expressions ## 🎯 Learning Objectives - Write lambda expressions for functional interfaces - Use the Stream API for declarative data processing - Chain intermediate and terminal stream operations - Understand Optional for null-safe code - Compare Java streams with Python comprehensions ## 1. L...

Streams API & Lambda Expressions
🎯 Learning Objectives
- Write lambda expressions for functional interfaces
- Use the Stream API for declarative data processing
- Chain intermediate and terminal stream operations
- Understand Optional for null-safe code
- Compare Java streams with Python comprehensions
1. Lambda Expressions — Passing Behavior
1.1 The Problem
Before Java 8, passing behavior required verbose anonymous inner classes:
java// Pre-Java 8 — verbose Collections.sort(list, new Comparator<String>() { @Override public int compare(String a, String b) { return a.length() - b.length(); } });
1.2 Lambda Solution
java// Java 8+ — concise Collections.sort(list, (a, b) -> a.length() - b.length());
Lambda syntax:
pseudo(parameters) -> expression (parameters) -> { statements; }
1.3 Lambda Variants
java// Zero parameters () -> System.out.println("Hello") // One parameter (parens optional) x -> x * x // Multiple parameters (a, b) -> a + b // Multiple statements (a, b) -> { int sum = a + b; System.out.println(sum); return sum; }
1.4 Functional Interfaces (SAM — Single Abstract Method)
java@FunctionalInterface interface Calculator { int operate(int a, int b); } // Usage: Calculator add = (a, b) -> a + b; Calculator multiply = (a, b) -> a * b; System.out.println(add.operate(5, 3)); // 8 System.out.println(multiply.operate(5, 3)); // 15
Built-in functional interfaces:
| Interface | Method | Signature |
|---|---|---|
Predicate | test(T) | T → boolean |
Function | apply(T) | T → R |
Consumer | accept(T) | T → void |
Supplier | get() | () → T |
UnaryOperator | apply(T) | T → T |
BinaryOperator | apply(T,T) | (T,T) → T |
2. Method References
Shorthand when lambda just calls an existing method:
java// Lambda list.forEach(s -> System.out.println(s)); // Method reference list.forEach(System.out::println); // Other forms: String::length // instance method on arbitrary object "Hello"::length // instance method on specific object Math::sqrt // static method String::new // constructor new int[5]::clone // constructor reference for arrays
3. Stream API
3.1 Creating Streams
java// From collections List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream = list.stream(); // From arrays Stream<Integer> arrStream = Arrays.stream(new Integer[]{1, 2, 3}); // From values Stream<String> of = Stream.of("A", "B", "C"); // Infinite streams Stream<Integer> evens = Stream.iterate(0, n -> n + 2); Stream<Double> randoms = Stream.generate(Math::random);
3.2 Intermediate Operations (Lazy)
javaList<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry"); // filter — keep elements matching predicate words.stream() .filter(s -> s.length() > 5) .forEach(System.out::println); // banana, cherry, elderberry // map — transform each element words.stream() .map(String::toUpperCase) .forEach(System.out::println); // APPLE, BANANA, ... // flatMap — flatten nested streams List<List<Integer>> nested = Arrays.asList( Arrays.asList(1, 2), Arrays.asList(3, 4, 5)); nested.stream() .flatMap(Collection::stream) .forEach(System.out::println); // 1, 2, 3, 4, 5 // distinct, sorted, peek, limit, skip words.stream() .filter(s -> s.startsWith("a")) .map(String::toUpperCase) .sorted() .limit(2) .forEach(System.out::println);
3.3 Terminal Operations (Eager)
java// collect — accumulate into collection List<String> result = words.stream() .filter(s -> s.length() > 4) .collect(Collectors.toList()); // reduce — combine elements int sum = Arrays.asList(1, 2, 3, 4, 5).stream() .reduce(0, (a, b) -> a + b); // 15 // counting, groupingBy, partitioningBy Map<Integer, List<String>> byLength = words.stream() .collect(Collectors.groupingBy(String::length)); // findFirst, findAny, allMatch, anyMatch, noneMatch boolean hasLongWord = words.stream().anyMatch(s -> s.length() > 10);
3.4 Stream Pipeline Pattern
javaint result = numbers.stream() // Source .filter(n -> n % 2 == 0) // Intermediate (lazy) .map(n -> n * n) // Intermediate (lazy) .sorted(Comparator.reverseOrder()) // Intermediate (lazy) .findFirst() // Terminal (eager) .orElse(0); // Terminal result
4. Optional — Null Safety
java// Before Optional — null-prone String name = findName(id); if (name != null) { System.out.println(name.toUpperCase()); } // With Optional Optional<String> optional = findNameOptional(id); optional.map(String::toUpperCase) .ifPresent(System.out::println); // Creating Optional Optional<String> empty = Optional.empty(); Optional<String> nonNull = Optional.of("Hello"); // NullPointerException if null Optional<String> nullable = Optional.ofNullable(maybeNull); // Operations optional.isPresent() // boolean check optional.ifPresent(System.out::println) // consume if present optional.orElse("default") // value or default optional.orElseGet(() -> expensiveDefault()) // lazy default optional.orElseThrow(() -> new NotFoundException()) // or throw optional.map(String::length) // transform if present optional.filter(s -> s.length() > 5) // filter if present
5. Java vs Python
| Feature | Java | Python |
|---|---|---|
| Lambda | (x) -> x * x | lambda x: x * x |
| Filter | .filter(pred) | filter(func, iter) |
| Map | .map(func) | map(func, iter) |
| Reduce | .reduce(identity, op) | reduce(func, iter) |
| List comp | .collect(Collectors.toList()) | [x*2 for x in list] |
| Lazy streams | Yes (intermediate ops are lazy) | Yes (generators, itertools) |
| Null safety | Optional | Optional[T] (external lib) |
6. Practice Questions
Q1: What is the output?javaList<String> words = Arrays.asList("cat", "dog", "bird", "fish"); words.stream() .filter(w -> w.length() > 3) .map(String::toUpperCase) .forEach(System.out::println);Answer:BIRD FISH(cat and dog have length ≤ 3) Q2: Difference between intermediate and terminal operations?Answer: Intermediate operations (filter, map, sorted) are lazy — they don't execute until a terminal operation is called. Terminal operations (forEach, collect, reduce) trigger the pipeline and produce a result or side effect. Q3: What does Optional.of(null) do?Answer: ThrowsNullPointerException. UseOptional.ofNullable(null)if the value might be null. Q4: Write a lambda that squares a number and add it to a method.javaFunction<Integer, Integer> square = x -> x * x; System.out.println(square.apply(5)); // 25Q5: What is a method reference and when to use it?Answer: A method reference (ClassName::method) is shorthand for a lambda that calls an existing method. Use it when the lambda body is just a single method call:s -> s.length()becomesString::length. Q6: Can a stream be reused after a terminal operation?Answer: No. After a terminal operation, the stream is consumed. Calling another operation on it throwsIllegalStateException. Create a new stream for each pipeline. Q7: What does Collectors.groupingBy do?Answer: Groups elements by a classifier function, producing aMap<K, List<V>>:javaMap<Integer, List<String>> byLen = words.stream() .collect(Collectors.groupingBy(String::length));Q8: What is the difference between map and flatMap?Answer:maptransforms each element (1-to-1).flatMaptransforms each element into a stream, then flattens all streams (1-to-many-to-flat). Example: split strings into words.
📐 Key Concepts
| Feature | Purpose | Example |
|---|---|---|
| Lambda | Anonymous function | (x, y) -> x + y |
| Stream | Declarative data processing | list.stream().filter(...).map(...).collect(...) |
| Optional | Null-safe containers | Optional.ofNullable(val).orElse(default) |
| Method ref | Lambda shorthand | String::length |
| Collector | Accumulate stream → collection | Collectors.toList() |
🔗 Cross-References
- Next: Concurrency & Threads Join Discord Previous7.2 I/O Streams & SerializationNext10.1 Concurrency & Threads