An average needs a count and a sum, and teeing gets both from a single traversal. Here the group key itself comes from a joined list, so one pipeline does the join, the grouping and two aggregates.
record Emp(String name, String dept) {}
record Pay(String name, double amount) {}
var emps = List.of(new Emp("Ava", "Core"), new Emp("Ben", "Core"), new Emp("Cara", "Data"));
var pays = List.of(new Pay("Ava", 100.0), new Pay("Ben", 300.0), new Pay("Cara", 250.0));
Map<String, String> deptOf = emps.stream().collect(Collectors.toMap(Emp::name, Emp::dept));
Map<String, String> summary = pays.stream()
.collect(Collectors.groupingBy(p -> deptOf.get(p.name()), TreeMap::new,
Collectors.teeing(Collectors.counting(),
Collectors.summingDouble(Pay::amount),
(n, total) -> n + " people, total " + total + ", avg " + (total / n))));
summary.forEach((dept, s) -> System.out.println(dept + ": " + s));
Core: 2 people, total 400.0, avg 200.0
Data: 1 people, total 250.0, avg 250.0
Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.
Published 2026-09-20