ExerciseWarm-up
Warm-up
Break toMap three ways
10 minjunior1–8 yrs
One concept, guided. Near-impossible to fail.
What this teaches
- toMap throws IllegalStateException on a duplicate key
- toMap throws NullPointerException on a null value, unlike the map it builds
- groupingBy cannot have either problem, because its value is a collection
- The three-argument form makes the collision rule explicit
Starter
Starter.java
import java.util.*;
import java.util.stream.*;
/**
* Warm-up: make toMap fail on purpose, twice.
*
* toMap and groupingBy build a similar map from a stream and fail completely
* differently. Knowing the two failure modes by heart is the whole of this
* question in an interview, and the fastest way to know them is to cause them.
*/
public class Starter {
record Employee(String name, String dept, Integer bonus) { }
static final List<Employee> STAFF = List.of(
new Employee("Ana", "eng", 500),
new Employee("Bo", "eng", null),
new Employee("Cy", "sales", 250));
public static void main(String[] args) {
// TODO 1: predict what this does. Two employees, one department.
try {
Map<String, String> byDept = STAFF.stream()
.collect(Collectors.toMap(Employee::dept, Employee::name));
System.out.println("byDept : " + new TreeMap<>(byDept));
} catch (RuntimeException e) {
System.out.println("byDept : " + e.getClass().getSimpleName() + " — " + e.getMessage());
}
// TODO 2: fix it with the three-argument toMap. Write the merge
// function two ways — keep the first, and combine both — and say which
// one the business would actually want here.
// TODO 3: predict this one. Bo's bonus is null, and a HashMap would
// accept that happily.
try {
Map<String, Integer> bonuses = STAFF.stream()
.collect(Collectors.toMap(Employee::name, Employee::bonus));
System.out.println("bonuses: " + new TreeMap<>(bonuses));
} catch (RuntimeException e) {
System.out.println("bonuses: " + e.getClass().getSimpleName());
}
// TODO 4: prove to yourself that a plain HashMap.put accepts the same
// null. Then explain the difference — the answer is one method name.
// TODO 5: rewrite the first one as groupingBy so it cannot fail, and
// say in one sentence why the failure mode does not exist there.
// TODO 6: groupingBy(Employee::dept) gives you Map<String,
// List<Employee>>. Use a downstream collector to make it
// Map<String, Long> of headcount instead, without a second pass.
}
}Run it locally:
cd exercises/java/java8/collectors-and-grouping/01-warmup
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterDone when
- You produced both exceptions deliberately and read the messages
- You fixed the duplicate with a merge function and said what it chose
- You can explain why toMap rejects a null that HashMap.put accepts
- You rewrote one of the three as groupingBy and said why it cannot fail
← Back to How do Collectors.groupingBy and toMap differ in failure modes?