Collectors are the heart of stream questions
Most stream questions combine filter, map, sorted and a collector — groupingBy with a downstream collector, partitioningBy, joining or toMap. Read the requirement as a pipeline.
- GroupinggroupingBy with maxBy, counting, averaging.
- Transformingmap, flatMap, distinct and sorted.
- CollectingpartitioningBy, joining and toMap.
“Solve it with streams” is now standard in Java interviews at every level. The questions below use a simple record Employee(String name, String dept, double salary, int age) and cover the collectors and operations interviewers ask most.
1. Find the employee with the highest salary in each department.
groupingBy with a downstream maxBy returns an Optional per group; collectingAndThen unwraps it.
Map<String, Employee> topByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::dept,
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparingDouble(Employee::salary)),
Optional::get)));2. Find the second highest salary.
Map to salaries, remove duplicates, sort descending, skip one.
Optional<Double> second = employees.stream()
.map(Employee::salary)
.distinct()
.sorted(Comparator.reverseOrder())
.skip(1)
.findFirst();3. Count the frequency of each character in a string.
Turn the string into a stream of characters and count with groupingBy. A LinkedHashMap keeps first-seen order.
Map<Character, Long> freq = "learnfrenzy".chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()));
// {l=1, e=2, a=1, r=2, n=2, f=1, z=1, y=1}4. Find duplicate elements in a list.
Add to a Set: add returns false for items already seen.
Set<Integer> seen = new HashSet<>();
List<Integer> duplicates = List.of(4, 2, 7, 4, 9, 2, 4).stream()
.filter(n -> !seen.add(n))
.distinct()
.toList(); // [4, 2]5. Average salary per department, sorted by average descending.
averagingDouble as the downstream collector, then sort the map entries.
employees.stream()
.collect(Collectors.groupingBy(Employee::dept, Collectors.averagingDouble(Employee::salary)))
.entrySet().stream()
.sorted(Map.Entry.<String, Double>comparingByValue().reversed())
.forEach(e -> System.out.printf("%s: %.0f%n", e.getKey(), e.getValue()));6. Split employees into two groups: above and below 30 years old.
partitioningBy always returns a map with true and false keys.
Map<Boolean, List<String>> byAge = employees.stream()
.collect(Collectors.partitioningBy(e -> e.age() > 30,
Collectors.mapping(Employee::name, Collectors.toList())));7. Get all unique skills from a list of candidates, sorted.
flatMap flattens each candidate's skill list into one stream.
List<String> skills = candidates.stream()
.flatMap(c -> c.skills().stream())
.map(String::toLowerCase)
.distinct()
.sorted()
.toList();8. Join employee names in a department into one comma-separated string.
Collectors.joining accepts a delimiter, prefix and suffix.
String names = employees.stream()
.filter(e -> e.dept().equals("IT"))
.map(Employee::name)
.sorted()
.collect(Collectors.joining(", ", "[", "]")); // [Asha, Meera, Ravi]Remember
Key points
- Downstream collectors
Turn groups into counts, maxima or lists.
- flatMap flattens
Lists of lists into one stream.
- toList() is unmodifiable
Java 16+ Stream.toList().
- Optional from max/min
Unwrap with collectingAndThen.
Interview drill
Follow-up questions
What the interviewer asks after your first answer. Turn on practice mode, answer each one aloud, then reveal.
-
1 How do you convert a list to a map and handle duplicate keys? Intermediate
Use the three-argument toMap with a merge function.
Map<String, Double> salaryByName = employees.stream() .collect(Collectors.toMap(Employee::name, Employee::salary, Double::sum)); -
2 How do you get the first non-repeated character with streams? Intermediate
Count with groupingBy into a LinkedHashMap, then find the first entry with value 1.
-
3 Sum all salaries. Basic
employees.stream().mapToDouble(Employee::salary).sum(). -
4 Find the longest string in a list. Basic
list.stream().max(Comparator.comparingInt(String::length)). -
5 Reverse each word in a sentence with streams. Basic
Split on spaces, map each word through
new StringBuilder(w).reverse(), join with spaces. -
6 Why can't you reuse a stream? Basic
A stream is consumed by its terminal operation; using it again throws IllegalStateException. Create a new stream from the source.