Group Employees by Department
Java coding interview problem for Collections: Group Employees by Department.
Grouping objects is one of the most common problems in Java Collections interviews.
The problem teaches an important data processing pattern:
Collection of Objects
↓
Choose Grouping Criteria
↓
Create Groups
↓
Process Grouped Data
In real applications, data is rarely stored as a simple list.
Usually, we need to organize data based on:
- Department
- Location
- Category
- Type
- Status
- Date
What is Grouping Objects?
Grouping means collecting objects that share a common attribute.
Example:
Employee list:
John IT
Alice HR
Bob IT
David Finance
Group by department:
IT
John
Bob
HR
Alice
Finance
David
Understanding Employee Grouping Concept
An employee object contains multiple properties:
Employee
|
|-- id
|
|-- name
|
|-- department
|
|-- salary
We select one property as the grouping key.
Example:
department
becomes:
Map Key
Employees become:
Map Value
Map Structure for Grouping
Grouping uses:
Map<Key, List<Value>>
Example:
Department → Employees
Data structure:
HashMap
|
+---- IT
| |
| + John
| + Bob
|
|
+---- HR
| |
| + Alice
Key-Value Relationship
Input:
Employee(
101,
John,
IT
)
Stored as:
IT
↓
[
John
]
Another employee:
Employee(
102,
Bob,
IT
)
Updated:
IT
↓
[
John,
Bob
]
Why HashMap is Used for Grouping?
HashMap provides:
Fast Lookup
Average:
O(1)
When adding an employee:
Check:
Does department exist?
If yes:
Add employee to existing list
If no:
Create new list
Real-World Applications
Employee Management Systems
Group employees by:
- Department
- Location
- Team
Example:
Engineering
Sales
HR
Banking Applications
Group:
Customers
↓
Account Type
Example:
Savings
Checking
Business
E-Commerce Systems
Group products by:
- Category
- Brand
- Price range
Data Analytics
Group events by:
- Date
- Region
- User type
Problem Statement
Given a list of employees, group employees based on their department.
Employee Class Design
Employee fields:
id
name
department
salary
Employee Class
class Employee {
private int id;
private String name;
private String department;
private double salary;
public Employee(
int id,
String name,
String department,
double salary) {
this.id = id;
this.name = name;
this.department = department;
this.salary = salary;
}
public String getDepartment() {
return department;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
@Override
public String toString() {
return name +
" - " +
department +
" - " +
salary;
}
}
Input Example
Employees:
[
John IT 90000,
Alice HR 70000,
Bob IT 95000,
David Finance 80000
]
Expected Output
IT
[
John,
Bob
]
HR
[
Alice
]
Finance
[
David
]
Grouping Visualization
Input:
John IT
Alice HR
Bob IT
David HR
Initial:
{}
Read:
John IT
Map:
IT → [John]
Read:
Alice HR
Map:
IT → [John]
HR → [Alice]
Read:
Bob IT
Map:
IT → [John,Bob]
HR → [Alice]
Read:
David HR
Final:
IT → [John,Bob]
HR → [Alice,David]
HashMap Internal Working
When grouping:
map.put(department, employeeList);
Java performs:
Department
↓
hashCode()
↓
Bucket
↓
Store List
Example:
IT
hashCode()
Bucket 5
Approach 1 — Manual Grouping Using Loops
The basic approach:
- Create HashMap.
- Traverse employees.
- Check department.
- Add employee.
Algorithm
For every employee:
Get department
↓
Check existing group
↓
Create or add
Java Program — Manual Grouping
import java.util.*;
public class GroupEmployeesManual {
public static Map<String,List<Employee>>
groupEmployees(
List<Employee> employees) {
Map<String,List<Employee>> map =
new HashMap<>();
for(Employee employee :
employees) {
String department =
employee.getDepartment();
if(!map.containsKey(department)) {
map.put(
department,
new ArrayList<>()
);
}
map.get(department)
.add(employee);
}
return map;
}
}
Step-by-Step Explanation
Input:
John IT
Alice HR
Bob IT
Start:
{}
Process John:
Department:
IT
Create:
IT → [John]
Process Alice:
Department:
HR
Create:
HR → [Alice]
Process Bob:
Department:
IT
Existing group found.
Add:
IT → [John,Bob]
Using computeIfAbsent()
Java provides a cleaner approach.
Instead of:
if(!map.containsKey(key))
use:
map.computeIfAbsent()
Java Program
public static Map<String,List<Employee>>
groupEmployees(
List<Employee> employees) {
Map<String,List<Employee>> map =
new HashMap<>();
for(Employee employee :
employees) {
map.computeIfAbsent(
employee.getDepartment(),
key -> new ArrayList<>()
)
.add(employee);
}
return map;
}
How computeIfAbsent Works
Example:
Department:
IT
First employee:
IT does not exist
Create:
IT → new ArrayList()
Add employee.
Second employee:
IT exists
Use existing list.
Add employee.
Complexity Analysis
For:
n employees
Each employee is processed once.
Time:
O(n)
Space:
O(n)
because all employees are stored in groups.
Advantages
- Simple.
- Efficient.
- Uses standard Java collections.
- Easy to extend.
Drawbacks
- More boilerplate code.
- Manual list creation.
- Requires HashMap handling.
Approach 2 — Java 8 Stream groupingBy()
Java 8 introduced a powerful Collector:
Collectors.groupingBy()
It simplifies object grouping operations.
groupingBy() Concept
The pattern:
Collection
↓
Stream
↓
groupingBy()
↓
Map<Key,List<Value>>
Basic Syntax
Collectors.groupingBy(
Employee::getDepartment
)
This creates:
Map<String,List<Employee>>
where:
Key = Department
Value = Employees
Java Program — Group Employees by Department
import java.util.*;
import java.util.stream.Collectors;
public class GroupEmployeesUsingStreams {
public static Map<String,List<Employee>>
groupEmployees(
List<Employee> employees) {
return employees.stream()
.collect(
Collectors.groupingBy(
Employee::getDepartment
)
);
}
}
Output Example
Input:
John IT
Alice HR
Bob IT
David Finance
Output:
IT
[
John,
Bob
]
HR
[
Alice
]
Finance
[
David
]
Step-by-Step Execution
Input:
[
John IT,
Alice HR,
Bob IT
]
Create Stream:
John IT
Alice HR
Bob IT
Grouping Key:
Employee::getDepartment
First Employee:
Department = IT
Create:
IT → [John]
Second Employee:
Department = HR
Create:
HR → [Alice]
Third Employee:
Department = IT
Existing group:
IT → [John]
Add:
IT → [John,Bob]
Group Employees by Department Count
A common interview question:
How many employees are in each department?
Expected:
IT → 2
HR → 1
Finance → 1
Using counting()
Map<String,Long> countByDepartment =
employees.stream()
.collect(
Collectors.groupingBy(
Employee::getDepartment,
Collectors.counting()
)
);
Output
IT = 2
HR = 1
Finance = 1
Find Highest Salary Employee Per Department
Another common variation:
Find the highest paid employee in each department.
Example:
Input:
John IT 90000
Bob IT 120000
Alice HR 80000
Output:
IT → Bob
HR → Alice
Java Program
Map<String,Optional<Employee>>
highestSalary =
employees.stream()
.collect(
Collectors.groupingBy(
Employee::getDepartment,
Collectors.maxBy(
Comparator.comparing(
Employee::getSalary
)
)
)
);
Explanation
Grouping:
Department
Then:
Find maximum salary
inside each group.
Average Salary Per Department
Example:
IT
Average salary = 100000
Using averagingDouble()
Map<String,Double> averageSalary =
employees.stream()
.collect(
Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(
Employee::getSalary
)
)
);
Output
IT = 105000
HR = 80000
Grouping By Multiple Criteria
Real applications often require multiple grouping levels.
Example:
Department
+
Location
Employee data:
John
IT
Texas
Alice
IT
California
Result:
IT
|
|
+ Texas
|
+ California
Nested groupingBy()
Map<String,
Map<String,List<Employee>>>
result =
employees.stream()
.collect(
Collectors.groupingBy(
Employee::getDepartment,
Collectors.groupingBy(
Employee::getLocation
)
)
);
Multiple Grouping Example
Input:
John IT Texas
Bob IT Texas
Alice HR California
Output:
IT
Texas
John
Bob
HR
California
Alice
Group Employees By Salary Range
Example:
Salary categories:
LOW
MEDIUM
HIGH
Use custom classifier:
employees.stream()
.collect(
Collectors.groupingBy(
employee -> {
if(employee.getSalary() < 50000)
return "LOW";
else if(employee.getSalary() < 100000)
return "MEDIUM";
else
return "HIGH";
}
)
);
Grouping With LinkedHashMap
Default:
HashMap
does not guarantee order.
If insertion order is required:
Collectors.groupingBy(
Employee::getDepartment,
LinkedHashMap::new,
Collectors.toList()
)
Grouping With TreeMap
If sorted department names are required:
Collectors.groupingBy(
Employee::getDepartment,
TreeMap::new,
Collectors.toList()
)
HashMap vs LinkedHashMap vs TreeMap
| Feature | HashMap | LinkedHashMap | TreeMap |
|---|---|---|---|
| Order | No guarantee | Insertion order | Sorted order |
| Performance | O(1) | O(1) | O(log n) |
| Grouping Default | Yes | Optional | Optional |
| Use Case | Fast grouping | Ordered reports | Sorted reports |
Collectors API Deep Dive
Common collectors:
toList()
Collect elements:
Collectors.toList()
counting()
Count elements:
Collectors.counting()
averagingDouble()
Calculate average:
Collectors.averagingDouble()
maxBy()
Find maximum:
Collectors.maxBy()
minBy()
Find minimum:
Collectors.minBy()
mapping()
Transform grouped values.
Example:
Group employee names:
Collectors.groupingBy(
Employee::getDepartment,
Collectors.mapping(
Employee::getName,
Collectors.toList()
)
)
Custom Object Grouping
Grouping is not limited to Employee.
Example:
Product:
id
name
category
Group:
category → products
Example:
Map<String,List<Product>> products =
list.stream()
.collect(
Collectors.groupingBy(
Product::getCategory
)
);
Primitive vs Object Collections
Java Collections work with objects.
Cannot:
Map<int,List<int>>
Use:
Map<Integer,List<Integer>>
Autoboxing:
int
↓
Integer
Common Interview Mistakes
Mistake 1
Using loops unnecessarily.
Modern Java:
groupingBy()
is cleaner.
Mistake 2
Wrong grouping key.
Example:
Need:
department
but grouping by:
name
creates incorrect output.
Mistake 3
Ignoring order requirements.
Need sorted groups?
Use:
TreeMap
Mistake 4
Using groupingBy when only counting is required.
For counts:
counting()
is better.
Edge Cases
| Case | Handling |
|---|---|
| Empty employee list | Return empty map |
| One department | Single group |
| One employee | One element list |
| Null department | Handle separately |
| Large data | Stream processing |
Interview Follow-up Questions
Q1. Group employees by department.
Q2. Count employees in each department.
Q3. Find highest salary employee per department.
Q4. Find average salary by department.
Q5. Group employees by department and location.
Q6. Difference between groupingBy and partitioningBy.
Q7. Preserve order while grouping.
Q8. Sort employees inside each department.
Related Java Collection Problems
- Count Word Frequency Using HashMap
- Sort Employees by Salary
- Remove Duplicate Objects
- Find Duplicate Elements Using Set
- Find Top K Frequent Elements
- Group Anagrams
- Partition Numbers Using Predicate
Key Takeaways
Employee grouping follows this pattern:
List<Employee>
↓
Choose Grouping Key
↓
Map<Key,List<Employee>>
↓
Process Groups
Recommended approaches:
Simple grouping
Use:
HashMap
Modern Java approach
Use:
Collectors.groupingBy()
Ordered grouping
Use:
LinkedHashMap
or
TreeMap
Complexity:
For:
n employees
Time:
O(n)
Space:
O(n)
Frequently Asked Interview Questions
Q1. What does groupingBy return?
A:
Map<K,List<T>>
Q2. What is the difference between groupingBy and partitioningBy?
groupingBy:
Multiple groups
partitioningBy:
Two groups (true/false)
Q3. How do you find highest salary employee per department?
Use:
groupingBy()
+
maxBy()
Q4. How do you maintain order while grouping?
Use:
LinkedHashMap
Interview Tip
When asked:
"Group employees by department in Java."
Explain:
- Choose grouping key.
- Use Map of department to employee list.
- Implement with HashMap or groupingBy().
- Discuss advanced operations:
- Count employees.
- Average salary.
- Maximum salary.
- Nested grouping.
For senior Java interviews, discuss:
- HashMap internals.
- Collectors API.
- Stream processing.
- Performance considerations.
This demonstrates strong understanding of Java Collections, Stream API, and enterprise data processing patterns.