Back to Stream Practice: Employee Analysis
Q30Question 30 of 43

Find the year in which the maximum number of employees joined

Key Concepts
  • groupingBy(e -> e.getJoinDate().getYear(), counting()) produces Map<Integer, Long> of joins per year
  • max(comparingByValue()) finds the year with the most joins
  • This is the standard group-count-max pattern applied to a date component
  • Handle ties if multiple years have the same max count
  • getYear() extracts the year component from a LocalDate
  • Sort the result by year for a chronological hiring trend report

Definition

Group by the year component of the joining date with counting(), then find the max entry by value.
DIAGRAM
- **groupingBy(e -> e.getJoinDate().getYear(), counting())** produces `Map<Integer, Long>` of joins per year.
- **max(comparingByValue())** on the entrySet finds the peak hiring year.
- Same **group-count-max** pattern as department and city questions.

The bank's annual report asks: 'In which year did we hire the most people?' Count joiners per year, then find the peak year.

Understanding the Concepts

How do you find the peak hiring year?

JAVA
package com.bank.hr;

import java.util.*;
import java.util.stream.Collectors;

public class PeakHiringYear {
    public static void main(String[] args) {
        List<Employee> employees = Employee.getSampleEmployees();

        Map<Integer, Long> joinsByYear = employees.stream()
            .collect(Collectors.groupingBy(
                e -> e.getJoinDate().getYear(),
                Collectors.counting()));

        Optional<Map.Entry<Integer, Long>> peak = joinsByYear.entrySet()
            .stream()
            .max(Map.Entry.comparingByValue());

        peak.ifPresent(e ->
            System.out.println("Peak year: " + e.getKey()
                + " (" + e.getValue() + " hires)"));

        // Chronological trend
        new TreeMap<>(joinsByYear).forEach((year, count) ->
            System.out.println("  " + year + ": " + count));
    }
}
TEXT
Peak year: 2021 (3 hires)
  2005: 1
  2015: 1
  2018: 1
  2019: 1
  2021: 3
  2022: 1
  2023: 1
  2024: 1

What are common mistakes?

Mistake: using getYear() on a null joinDate.

JAVA
// Guard against null join dates
Map<Integer, Long> safe = employees.stream()
    .filter(e -> e.getJoinDate() != null)
    .collect(Collectors.groupingBy(
        e -> e.getJoinDate().getYear(),
        Collectors.counting()));

What about showing a chronological hiring trend?

JAVA
new TreeMap<>(joinsByYear).forEach((year, count) -> {
    String bar = "#".repeat(count.intValue());
    System.out.printf("%d: %s (%d)%n", year, bar, count);
});

What about finding the quarter with the most hires?

JAVA
import java.time.temporal.IsoFields;

Map<Integer, Long> joinsByQuarter = employees.stream()
    .filter(e -> e.getJoinDate() != null)
    .collect(Collectors.groupingBy(
        e -> e.getJoinDate().get(IsoFields.QUARTER_OF_YEAR),
        Collectors.counting()));

What about detecting hiring gaps?

JAVA
TreeMap<Integer, Long> sorted = new TreeMap<>(joinsByYear);
int firstYear = sorted.firstKey();
int lastYear = sorted.lastKey();
for (int year = firstYear; year <= lastYear; year++) {
    if (!sorted.containsKey(year)) {
        System.out.println("No hires in: " + year);
    }
}

What about grouping by month instead of year?

JAVA
import java.time.Month;

Map<Month, Long> joinsByMonth = employees.stream()
    .filter(e -> e.getJoinDate() != null)
    .collect(Collectors.groupingBy(
        e -> e.getJoinDate().getMonth(),
        Collectors.counting()));

What are the edge cases?

If no employees have a join date, the map is empty and max returns Optional.empty(). Guard against null joinDate before calling getYear().

What should a senior engineer know?

At National Bank of India, Rajesh builds a workforce planning report that identifies peak hiring years for budget forecasting. The HR team for account operations (account SAV2001 processing team) uses this data to plan onboarding capacity.

JAVA
// Production pattern: peak year with headcount and average salary
Map<Integer, DoubleSummaryStatistics> yearStats = employees.stream()
    .filter(e -> e.getJoinDate() != null)
    .collect(Collectors.groupingBy(
        e -> e.getJoinDate().getYear(),
        Collectors.summarizingDouble(
            e -> e.getSalary().doubleValue())));

yearStats.forEach((year, stats) ->
    System.out.printf("%d: %d hires, avg salary: %.0f%n",
        year, stats.getCount(), stats.getAverage()));

For large datasets, groupingByConcurrent with a parallel stream avoids the merge overhead. Use TreeMap as the map factory (groupingBy(classifier, TreeMap::new, counting())) when you need sorted output without a separate sort step.

What are common interview follow-up questions?

  • "How do you show a hiring trend over time?" Collect to TreeMap for chronological order.
  • "How do you find the quarter with the most hires?" Use getJoinDate().get(IsoFields.QUARTER_OF_YEAR).

Visual Overview

DIAGRAM
  groupingBy(joinYear, counting())
  ┌────────────────────────┐
  │  2005 → 1              │
  │  2015 → 1              │
  │  2021 → 3  ← peak     │
  │  2022 → 1              │
  │  2024 → 1              │
  └────────────────────────┘
  max(comparingByValue()) → 2021

Key Takeaway

Finding the peak hiring year uses groupingBy with the year extracted from the join date as the classifier and counting as the downstream, then max by value. Collect to a TreeMap for chronological display.

Remember one sentence: groupingBy(joinYear, counting()) then max by value finds the year with the most hires.

---

Share
Use ← → arrow keys to navigate