Appearance
Lesson 03 · Reduction & Collectors
Objectives
After this lesson you will be able to:
- Reduce a stream with
reduceand the identity/accumulator forms. - Collect into lists, sets, and maps with
Collectors. - Group and partition with downstream collectors.
reduce
reduce folds a stream to a single value. The common forms:
java
Optional<Integer> sum = Stream.of(1,2,3).reduce(Integer::sum); // Optional[6]
int sum2 = Stream.of(1,2,3).reduce(0, Integer::sum); // 6 (identity → no Optional)
String joined = Stream.of("a","b").reduce("", String::concat); // "ab"Gotcha
With an identity the result is the element type (no Optional); without one it returns Optional (empty stream → empty). The identity must be a true neutral element (0 for sum, 1 for product, "" for concat), or results are wrong.
collect and Collectors
collect accumulates into a container using a Collector:
java
List<String> list = stream.collect(Collectors.toList()); // or stream.toList() (immutable)
Set<String> set = stream.collect(Collectors.toSet());
String csv = stream.collect(Collectors.joining(", ", "[", "]"));
Map<String,Integer> m = stream.collect(Collectors.toMap(String::toUpperCase, String::length));Exam trap
Collectors.toMap throws IllegalStateException on a duplicate key unless you pass a merge function: toMap(k, v, (a, b) -> a). Also, Stream.toList() (Java 16+) returns an unmodifiable list, whereas Collectors.toList() makes no guarantee about mutability.
Grouping and partitioning
java
Map<Integer,List<String>> byLen =
stream.collect(Collectors.groupingBy(String::length));
Map<Boolean,List<Integer>> parts =
nums.collect(Collectors.partitioningBy(n -> n % 2 == 0)); // keys: true & falseDownstream collectors transform each group:
java
Map<Integer,Long> countByLen =
stream.collect(Collectors.groupingBy(String::length, Collectors.counting()));
Map<Dept,Double> avgSalary =
emps.collect(Collectors.groupingBy(Employee::dept,
Collectors.averagingDouble(Employee::salary)));SDET note
partitioningBy always yields both true and false keys (even if a side is empty), while groupingBy only has keys that occur — a subtle difference when asserting map contents in a test.
Key Takeaways
reduce(identity, op)returns the element type;reduce(op)returns anOptional. The identity must be a real neutral element.collectwithCollectors:toList/toSet/joining/toMap.toMapneeds a merge function for duplicate keys or it throws.Stream.toList()is unmodifiable;Collectors.toList()is unspecified.groupingBy(keys that occur) andpartitioningBy(alwaystrue+false) accept downstream collectors likecounting,mapping,averagingDouble.
Lesson Quiz
What does Stream.of(1,2,3).reduce(Integer::sum) return?
What does toMap do on a duplicate key with no merge function?
Which keys does partitioningBy(p) always produce?
How do you count strings per length?
What is the result type of list.stream().toList()?
Next: Optional & Primitive Streams. Run the matching code in labs/src/main/java/com/jse21/m06_streams/.