Definition
- Org-wide:
groupingBy(Employee::getGender, counting())producesMaplike{M:5, F:5}. - Per-department:
groupingBy(dept, groupingBy(gender, counting()))producesMap.> - partitioningBy:
partitioningBy(predicate, counting())producesMapwith guaranteed true/false keys. - summingDouble:
groupingBy(gender, summingDouble(Employee::getSalary))gives total salary per gender.
Organization-wide: how many male and female employees in the entire bank? One Map with two entries. Per-department: how many male and female in EACH department? Nested Map: outer key is department, inner key is gender.
Understanding the Concepts
How do you count employees by gender?
package com.bank.service;
import java.util.*;
import java.util.stream.Collectors;
public class GenderCountDemo {
record Employee(String name, String department, String gender, double salary) {}
public static void main(String[] args) {
List<Employee> employees = List.of(
new Employee("Rajesh", "IT", "M", 85000),
new Employee("Priya", "IT", "F", 72000),
new Employee("Amit", "Finance", "M", 95000),
new Employee("Deepa", "Finance", "F", 110000),
new Employee("Vikram", "IT", "M", 78000),
new Employee("Neha", "Finance", "F", 88000)
);
// Org-wide gender count
Map<String, Long> genderCount = employees.stream()
.collect(Collectors.groupingBy(Employee::gender, Collectors.counting()));
System.out.println("Gender count: " + genderCount);
}
}
Gender count: {M=3, F=3}
How do you count per department?
// Per-department gender count (nested groupingBy)
Map<String, Map<String, Long>> deptGender = employees.stream()
.collect(Collectors.groupingBy(Employee::department,
Collectors.groupingBy(Employee::gender, Collectors.counting())));
System.out.println("Dept gender: " + deptGender);
Dept gender: {IT={M=2, F=1}, Finance={M=1, F=2}}
How do you use partitioningBy for binary splits?
// Binary split with partitioningBy
Map<Boolean, Long> maleVsFemale = employees.stream()
.collect(Collectors.partitioningBy(
e -> "M".equals(e.gender()), Collectors.counting()));
System.out.println("Male vs Female: " + maleVsFemale);
// {true=3, false=3}
partitioningBy guarantees both true and false keys exist, even if one group is empty.
How do you get total salary per gender?
// Total salary per gender
Map<String, Double> salaryByGender = employees.stream()
.collect(Collectors.groupingBy(Employee::gender,
Collectors.summingDouble(Employee::salary)));
System.out.println("Salary by gender: " + salaryByGender);
// {M=258000.0, F=270000.0}
What is the difference between groupingBy and partitioningBy?
| Aspect | groupingBy | partitioningBy |
|--------|-----------|----------------|
| Classifier | Any function | Predicate (boolean) |
| Keys | Dynamic (as many as values) | Always 2: true and false |
| Missing key | Not present | Always present (empty list) |
| Use case | Multi-category grouping | Binary split |
How do you get average salary per gender?
// Average salary per gender
Map<String, Double> avgByGender = employees.stream()
.collect(Collectors.groupingBy(Employee::gender,
Collectors.averagingDouble(Employee::salary)));
System.out.println("Avg salary: " + avgByGender);
// {M=86000.0, F=90000.0}
How do you get names per gender per department?
// Names per gender per department (three-level)
Map<String, Map<String, List<String>>> detailed = employees.stream()
.collect(Collectors.groupingBy(Employee::department,
Collectors.groupingBy(Employee::gender,
Collectors.mapping(Employee::name, Collectors.toList()))));
System.out.println("IT males: " + detailed.get("IT").get("M"));
// [Rajesh, Vikram]
What are common mistakes?
// WRONG: using groupingBy for binary split (missing false key if no females)
Map<String, Long> bad = employees.stream()
.collect(Collectors.groupingBy(Employee::gender, Collectors.counting()));
// If all employees are male, key "F" is ABSENT
// CORRECT: partitioningBy guarantees both keys
Map<Boolean, Long> good = employees.stream()
.collect(Collectors.partitioningBy(e -> "M".equals(e.gender()), Collectors.counting()));
// Both true and false keys always present
What should a senior engineer know?
- partitioningBy guarantees both true and false keys exist in the result Map, even if one group is empty.
- groupingBy only includes keys that have at least one element.
- For binary splits (active/inactive, male/female, settled/unsettled), partitioningBy is cleaner.
- The nested groupingBy pattern is a common interview question demonstrating Collector composition.
- For gender analysis in production, use BigDecimal for salary calculations, not double.
Visual Overview
Gender counting patterns:
─────────────────────────
Org-wide:
groupingBy(gender, counting())
→ {M: 3, F: 3}
Per-department:
groupingBy(dept, groupingBy(gender, counting()))
→ {IT: {M: 2, F: 1}, Finance: {M: 1, F: 2}}
Binary split:
partitioningBy(isMale, counting())
→ {true: 3, false: 3}
Key Takeaway
Counting by gender organisation-wide uses groupingBy with counting as downstream. Per-department breakdown nests another groupingBy. For binary splits, partitioningBy is cleaner and guarantees both true and false keys exist.
The nested groupingBy pattern demonstrates Collector composition, a common interview exercise. Post-processing with another stream on the result Map enables further analysis.
Remember one sentence: groupingBy with counting for headcount, nested groupingBy for per-department breakdown, and partitioningBy for binary male/female split.
---