Back to Stream Practice: Employee Analysis
Q2Question 2 of 43

Count male vs female employees: across organization and per department

Key Concepts
  • Organization-wide: groupingBy(Employee::getGender, counting()) produces Map<String, Long> like {M:5, F:5}.
  • Per-department: nested groupingBy(dept, groupingBy(gender, counting())) produces Map<String, Map<String, Long>>.
  • partitioningBy is cleaner for binary splits: partitioningBy(Employee::isMale, counting()) guarantees both keys.
  • partitioningBy always has both true and false keys, even if one group is empty.
  • groupingBy with summingDouble as downstream gives total salary per gender per department.
  • These patterns demonstrate multi-level Collector composition, a common interview exercise.
  • Post-processing with another stream on the result Map enables analysis like flagging gender imbalance.

Definition

groupingBy(gender, counting()) for org-wide count; nested groupingBy(dept, groupingBy(gender, counting())) for per-department; partitioningBy for binary male/female split.
  • Org-wide: groupingBy(Employee::getGender, counting()) produces Map like {M:5, F:5}.
  • Per-department: groupingBy(dept, groupingBy(gender, counting())) produces Map>.
  • partitioningBy: partitioningBy(predicate, counting()) produces Map with 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?

JAVA
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);
    }
}
TEXT
Gender count: {M=3, F=3}

How do you count per department?

JAVA
// 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);
TEXT
Dept gender: {IT={M=2, F=1}, Finance={M=1, F=2}}

How do you use partitioningBy for binary splits?

JAVA
// 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?

JAVA
// 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?

JAVA
// 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?

JAVA
// 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?

JAVA
// 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

DIAGRAM
  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.

---

Share
Use ← → arrow keys to navigate