Back to Stream Practice: Employee Analysis
Q36Question 36 of 43

Find employees who share the same birthday month

Key Concepts
  • groupingBy(e -> e.getBirthDate().getMonth()) groups employees by their birth month using the Month enum
  • Filter the resulting map to keep only groups with size > 1, because sharing requires at least two people
  • Guard against null birth dates with a filter before grouping to avoid NullPointerException
  • Use TreeMap as the map supplier or sort by Month.ordinal() for chronological output
  • flatMap the filtered groups to get a flat list of employees who share a birthday month
  • For exact same birthday (month + day), group by MonthDay instead of Month

Definition

Group employees by birth month using `groupingBy(getBirthDate().getMonth())`, then keep only groups with more than one member to find birthday-month sharing.
  • groupingBy(getBirthDate().getMonth()) produces a Map> bucketed by the Month enum.
  • Filter size > 1 keeps only months where at least two employees share the birthday month.
  • flatMap flattens the matching groups into a single list of employees who share a month.
  • TreeMap supplier or sort by Month.ordinal() gives chronological output instead of hash order.
  • Null guard with filter(e -> e.getBirthDate() != null) prevents NullPointerException before grouping.

The National Bank of India HR team wants to throw one birthday celebration per month for employees sharing that month. Priya groups the staff roster by birth month, finds months with two or more people, and schedules a celebration for each qualifying month.

Understanding the Concepts

What does it mean to find employees sharing a birthday month?

Sharing a birthday month means two or more employees have a birth date in the same calendar month, regardless of the day or year. The java.time.Month enum (JANUARY through DECEMBER) provides the grouping key. Grouping by Month and then filtering for groups with more than one member identifies the months where multiple employees share birthdays.

This is a classic group-then-filter pattern that appears frequently in stream interview questions.

How do you implement it with streams?

The solution has three parts: guard nulls, group by month, and filter for shared months.

JAVA
package com.bank.hr;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.Month;
import java.util.*;
import java.util.stream.Collectors;

public class SharedBirthdayMonth {

    record Employee(String id, String name, LocalDate birthDate,
                    String department, BigDecimal salary) {}

    public static void main(String[] args) {
        List<Employee> staff = List.of(
            new Employee("SAV2001", "Rajesh",  LocalDate.of(1989, 1, 22), "TRADING",    new BigDecimal("1800000")),
            new Employee("SAV2002", "Priya",   LocalDate.of(1995, 1, 5),  "RISK",       new BigDecimal("1200000")),
            new Employee("SAV2003", "Amit",    LocalDate.of(1990, 3, 15), "COMPLIANCE", new BigDecimal("900000")),
            new Employee("SAV2004", "Kavita",  LocalDate.of(1985, 7, 10), "OPERATIONS", new BigDecimal("1100000")),
            new Employee("SAV2005", "Meera",   LocalDate.of(1986, 7, 25), "TRADING",    new BigDecimal("1500000")),
            new Employee("SAV2006", "Ananya",  LocalDate.of(1992, 5, 8),  "RISK",       new BigDecimal("1050000"))
        );

        // Step 1: Group by birth month, guarding nulls
        Map<Month, List<Employee>> byMonth = staff.stream()
            .filter(e -> e.birthDate() != null)
            .collect(Collectors.groupingBy(e -> e.birthDate().getMonth()));

        // Step 2: Keep only months with 2+ employees
        Map<Month, List<Employee>> shared = byMonth.entrySet().stream()
            .filter(entry -> entry.getValue().size() > 1)
            .collect(Collectors.toMap(
                Map.Entry::getKey, Map.Entry::getValue,
                (a, b) -> a, TreeMap::new));  // TreeMap for chronological order

        // Step 3: Display
        shared.forEach((month, employees) -> {
            System.out.println(month + ":");
            employees.forEach(e -> System.out.println(
                "  " + e.name() + " (" + e.birthDate() + ")"));
        });
    }
}
TEXT
JANUARY:
  Rajesh (1989-01-22)
  Priya (1995-01-05)
JULY:
  Kavita (1985-07-10)
  Meera (1986-07-25)

How do you get a flat list of employees who share a month?

If you need a single List rather than the grouped map, use flatMap on the filtered groups.

JAVA
List<Employee> sharingEmployees = shared.values().stream()
    .flatMap(Collection::stream)
    .toList();

System.out.println("Employees sharing a birthday month: " + sharingEmployees.size());
TEXT
Employees sharing a birthday month: 4

How is grouping by Month different from grouping by MonthDay?

| Aspect | Month | MonthDay |
|---|---|---|
| Grouping key | Calendar month only (JANUARY) | Month + day (--01-22) |
| Matches | Same month, any day | Exact same month and day |
| Use case | Birthday month celebration | Exact birthday twins |
| Key type | java.time.Month enum | java.time.MonthDay |

JAVA
// Group by exact month-day for birthday twins
Map<java.time.MonthDay, List<Employee>> byMonthDay = staff.stream()
    .filter(e -> e.birthDate() != null)
    .collect(Collectors.groupingBy(
        e -> java.time.MonthDay.from(e.birthDate())));

What are common mistakes?

Mistake 1: Including months with only one employee.

JAVA
// WRONG: no filter, shows all months including singletons
byMonth.forEach((month, emps) -> {
    System.out.println(month + ": " + emps.size());
});

// CORRECT: filter for size > 1 to find actual sharing
Map<Month, List<Employee>> shared = byMonth.entrySet().stream()
    .filter(e -> e.getValue().size() > 1)
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Mistake 2: Not guarding null birth dates.

JAVA
// WRONG: NullPointerException if birthDate is null
staff.stream().collect(Collectors.groupingBy(
    e -> e.birthDate().getMonth()));  // throws NPE

// CORRECT: filter nulls first
staff.stream()
    .filter(e -> e.birthDate() != null)
    .collect(Collectors.groupingBy(e -> e.birthDate().getMonth()));

What about ordering the output chronologically?

The default groupingBy produces a HashMap, which has no guaranteed order. To get months in calendar order, use a TreeMap supplier.

JAVA
Map<Month, List<Employee>> byMonthOrdered = staff.stream()
    .filter(e -> e.birthDate() != null)
    .collect(Collectors.groupingBy(
        e -> e.birthDate().getMonth(),
        TreeMap::new,
        Collectors.toList()));
// Keys iterate in natural Month enum order: JANUARY, FEBRUARY, ...

What about edge cases?

Edge case 1: All employees born in the same month.

JAVA
// All in JANUARY: shared map has one entry with all employees

Edge case 2: No two employees share a month.

JAVA
// shared map is empty after filtering

Edge case 3: Employee list is empty.

JAVA
// byMonth is an empty map, shared is also empty, no output

What are common interview follow-up questions?

  • "How do you find employees sharing the exact same birthday (month + day)?" Group by MonthDay.from(birthDate) instead of Month.
  • "How do you count how many months have shared birthdays?" shared.size() gives the count directly.
  • "How do you find the month with the most shared birthdays?" shared.entrySet().stream().max(comparingInt(e -> e.getValue().size())).
  • "How do you handle employees with no birth date on file?" Filter them out before grouping, or collect them separately for a data-quality report.

Visual Overview

DIAGRAM
  Input: [Rajesh(Jan), Priya(Jan), Amit(Mar), Kavita(Jul), Meera(Jul), Ananya(May)]
       │
       │  filter(birthDate != null)
       │  groupingBy(birthDate.getMonth())
       ▼
  ┌──────────────────────────────────────────┐
  │  JANUARY  → [Rajesh, Priya]       (2) ✓ │
  │  MARCH    → [Amit]                (1) ✗ │
  │  MAY      → [Ananya]             (1) ✗ │
  │  JULY     → [Kavita, Meera]       (2) ✓ │
  └──────────────────────────────────────────┘
       │
       │  filter(size > 1)
       ▼
  ┌──────────────────────────────────────────┐
  │  JANUARY  → [Rajesh, Priya]              │
  │  JULY     → [Kavita, Meera]              │
  └──────────────────────────────────────────┘

Key Takeaway

Finding employees who share a birthday month is a straightforward group-then-filter problem. You group by the Month enum extracted from each employee's birth date, producing a map of month to employee list, and then filter that map to keep only the entries where the list has more than one member. The result tells you which months have celebrations for multiple employees.

At the National Bank of India, Priya uses this pattern to plan birthday celebrations. She guards against null birth dates before grouping, uses a TreeMap supplier so the output comes in chronological order from January to December, and flatMaps the result when she needs a single list of all sharing employees.

The pattern generalizes to any attribute: replace month with city, department, or salary band and the same group-then-filter shape applies. For exact same birthday (month and day), swap Month for MonthDay. For same full date including year, group by LocalDate itself.

Remember one sentence: groupingBy(birthMonth) then filter(size > 1) finds months where multiple employees share birthdays.

---

Share
Use ← → arrow keys to navigate