Definition
- Step 1:
groupingBy(Employee::getDepartment, counting())producesMapwith the headcount per department. - Step 2:
entrySet().stream().max(Map.Entry.comparingByValue())returnsOptionalcontaining the department with the highest count.> - Ties: If two departments have the same max count,
maxreturns one arbitrarily. To find all tied departments, compute maxCount first, then filter. - One-liner: Use
collectingAndThento chain both steps inside a singlecollectcall. - Smallest department: Replace
maxwithminin step 2.
The National Bank of India capacity planner asks: "Which department has the most employees?" First, count how many people sit on each floor of the building. That gives a table: Floor 1 has 5, Floor 2 has 8, Floor 3 has 3. Then scan the table for the highest number. Floor 2 wins with 8. If Floor 1 and Floor 2 both have 8, they are tied.
Understanding the Concepts
What does this question ask?
The interviewer wants you to find which department has the most employees. This is a two-step stream problem: first aggregate (group and count), then find the extreme (max on the aggregated map). This pattern appears frequently in interviews because it tests both groupingBy and entrySet streaming.
How do you count employees per department?
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class HeadcountByDept {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
Map<String, Long> countByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.counting()));
countByDept.forEach((dept, count) ->
System.out.println(dept + ": " + count));
}
}
TRADING: 3
RISK: 3
OPERATIONS: 2
COMPLIANCE: 2
How do you find the department with the highest count?
Stream the Map's entrySet and use max with Map.Entry.comparingByValue().
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class LargestDepartment {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
Map<String, Long> countByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.counting()));
Optional<Map.Entry<String, Long>> largest = countByDept.entrySet()
.stream()
.max(Map.Entry.comparingByValue());
largest.ifPresent(e ->
System.out.println("Largest: " + e.getKey()
+ " (" + e.getValue() + " employees)"));
}
}
Largest: TRADING (3 employees)
The result is Optional. Use map(Map.Entry::getKey) to extract just the department name.
How do you write it as a one-liner?
String largestDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment, Collectors.counting()))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(e -> e.getKey() + " (" + e.getValue() + " employees)")
.orElse("No departments");
System.out.println(largestDept);
TRADING (3 employees)
The one-liner chains collect, entrySet, stream, max, and map. In interviews, the two-step version is easier to explain.
How do you handle ties?
If two departments have the same count, max returns one arbitrarily. To find all tied departments, compute the max count first, then filter.
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class TiedDepartments {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
Map<String, Long> countByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment, Collectors.counting()));
long maxCount = countByDept.values().stream()
.mapToLong(Long::longValue)
.max()
.orElse(0);
List<String> tiedDepts = countByDept.entrySet().stream()
.filter(e -> e.getValue() == maxCount)
.map(Map.Entry::getKey)
.toList();
System.out.println("Max count: " + maxCount);
System.out.println("Tied departments: " + tiedDepts);
}
}
Max count: 3
Tied departments: [TRADING, RISK]
How do you rank all departments by size?
Sort the entrySet by value descending and collect to a LinkedHashMap to preserve order.
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class DepartmentRanking {
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
LinkedHashMap<String, Long> ranked = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment, Collectors.counting()))
.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> a,
LinkedHashMap::new));
int rank = 1;
for (var entry : ranked.entrySet()) {
System.out.println(rank++ + ". " + entry.getKey()
+ ": " + entry.getValue());
}
}
}
1. TRADING: 3
2. RISK: 3
3. OPERATIONS: 2
4. COMPLIANCE: 2
What are common mistakes?
Mistake 1: trying to find max on the original employee stream instead of the grouped map.
// WRONG: this finds the employee with the "largest" department name alphabetically
Optional<Employee> wrong = employees.stream()
.max(Comparator.comparing(Employee::getDepartment));
// CORRECT: group first, then find max count
Optional<Map.Entry<String, Long>> correct = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment, Collectors.counting()))
.entrySet().stream()
.max(Map.Entry.comparingByValue());
Mistake 2: forgetting that you need a second stream on the entrySet.
// WRONG: Map has no max method
// countByDept.max(...) // does not compile
// CORRECT: stream the entrySet
countByDept.entrySet().stream().max(Map.Entry.comparingByValue());
Mistake 3: using Collections.max instead of stream for the Map values.
// Works but less idiomatic:
Long maxVal = Collections.max(countByDept.values());
// Then you need a separate step to find which key has that value
// More idiomatic: stream the entrySet
countByDept.entrySet().stream()
.max(Map.Entry.comparingByValue());
How do you combine both steps using collectingAndThen?
Optional<Map.Entry<String, Long>> result = employees.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Employee::getDepartment, Collectors.counting()),
map -> map.entrySet().stream()
.max(Map.Entry.comparingByValue())));
This wraps the two-step logic inside a single collect call. The finisher function takes the grouped map and finds the max entry.
What are common interview follow-up questions?
- "How do you find the smallest department?" Replace max with min.
- "How do you find the department with the highest total salary?" Change the downstream to summingDouble(salary) instead of counting().
- "How do you find the department with the highest average salary?" Use averagingDouble(salary) as the downstream, then max on the values.
- "How do you exclude departments with fewer than 3 employees?" Filter the entrySet: filter(e -> e.getValue() >= 3).
Visual Overview
Step 1: Group and Count
Stream<Employee>
│
│ .collect(groupingBy(dept, counting()))
▼
┌──────────────────────────┐
│ Map<String, Long> │
│ TRADING ───→ 3 │
│ RISK ──────→ 3 │
│ OPERATIONS → 2 │
│ COMPLIANCE → 2 │
└──────────────────────────┘
Step 2: Find Max Entry
entrySet().stream()
│
│ .max(comparingByValue())
▼
Optional<Entry<"TRADING", 3>>
Key Takeaway
Finding the department with the most employees is a two-step stream operation. Step one groups by department with counting as the downstream: groupingBy(Employee::getDepartment, counting()) produces Map. Step two streams the entrySet and finds the max by value: entrySet().stream().max(Map.Entry.comparingByValue()).
The key insight is that groupingBy produces a Map, and you need a second stream on the Map's entrySet to find the max or min entry. The comparators Map.Entry.comparingByValue() and comparingByKey() are the standard tools for comparing map entries.
To handle ties where multiple departments share the same max count, find the maxCount first, then filter all entries matching that count. For a complete ranking, sort the entrySet by value descending and collect to a LinkedHashMap.
Remember one sentence: group by department with counting to get a Map of headcounts, then stream the entrySet and find max by value to identify the largest department.
---