Back to Stream: Grouping & Partitioning
Q15Question 15 of 22

Group employees by the first letter of their name

Key Concepts
  • groupingBy(e -> e.getName().charAt(0)) uses the first character as the grouping key
  • charAt(0) throws StringIndexOutOfBoundsException on an empty string, so filter or guard first
  • Normalise case with Character.toUpperCase so 'amit' and 'Amit' land in the same bucket
  • Supply TreeMap::new for alphabetical key order in the output
  • The classifier returns a Character, producing Map<Character, List<Employee>>
  • This pattern builds phone-book-style alphabetical directories and indexes

Definition

groupingBy(e -> e.getName().charAt(0)) groups employees by the first letter of their name, building a phone-book-style index.
  • e.getName().charAt(0) extracts the first character as the grouping key.
  • The result is Map> where each key is an initial letter.
  • charAt(0) throws on empty strings, so guard with a filter or null check.
  • Normalise with Character.toUpperCase(name.charAt(0)) so case does not split groups.
  • TreeMap::new gives alphabetical key order for display.

Priya at the National Bank of India files employees like a phone book: an A tray, a B tray, and so on, based on the first letter of their name.

Understanding the Concepts

How do you group by the first letter of the name?

The classifier extracts the first character of each name and uses it as the grouping key.

JAVA
package com.bank.hr;

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

public class GroupByInitial {
    public static void main(String[] args) {
        List<Employee> employees = List.of(
            new Employee("Rajesh"),
            new Employee("Priya"),
            new Employee("Amit"),
            new Employee("Kavita"),
            new Employee("Ananya"),
            new Employee("Neha")
        );

        Map<Character, List<Employee>> byInitial = employees.stream()
            .collect(Collectors.groupingBy(
                e -> Character.toUpperCase(
                    e.getName().charAt(0)),
                TreeMap::new,
                Collectors.toList()));

        byInitial.forEach((letter, emps) ->
            System.out.println(letter + ": " + emps.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(", "))));
    }
}
TEXT
A: Amit, Ananya
K: Kavita
N: Neha
P: Priya
R: Rajesh

Why normalise the first character?

Without normalisation, 'amit' and 'Amit' would be placed in different buckets because 'a' and 'A' are different characters.

JAVA
// WRONG: case-sensitive grouping
Map<Character, List<String>> caseSensitive = names.stream()
    .collect(Collectors.groupingBy(n -> n.charAt(0)));
// {A: [Amit], a: [amit]} - two separate groups!

// CORRECT: normalise to uppercase
Map<Character, List<String>> caseInsensitive = names.stream()
    .collect(Collectors.groupingBy(
        n -> Character.toUpperCase(n.charAt(0))));
// {A: [Amit, amit]} - one group

How do you guard against empty or null names?

charAt(0) throws on an empty string, and calling methods on null throws NullPointerException.

JAVA
// WRONG: crashes on empty or null names
Map<Character, List<Employee>> crash = employees.stream()
    .collect(Collectors.groupingBy(
        e -> e.getName().charAt(0)));  // NPE or SIOOBE!

// CORRECT: filter first
Map<Character, List<Employee>> safe = employees.stream()
    .filter(e -> e.getName() != null && !e.getName().isEmpty())
    .collect(Collectors.groupingBy(
        e -> Character.toUpperCase(e.getName().charAt(0)),
        TreeMap::new,
        Collectors.toList()));

How do you count employees per initial?

JAVA
Map<Character, Long> countPerInitial = employees.stream()
    .filter(e -> e.getName() != null && !e.getName().isEmpty())
    .collect(Collectors.groupingBy(
        e -> Character.toUpperCase(e.getName().charAt(0)),
        TreeMap::new,
        Collectors.counting()));

countPerInitial.forEach((letter, count) ->
    System.out.println(letter + ": " + count));
TEXT
A: 2
K: 1
N: 1
P: 1
R: 1

What should a senior engineer know?

  • The classifier must be total and robust: handle empty and null names before grouping.
  • Supplying a TreeMap gives alphabetical key order for display.
  • This pattern builds directories, indexes, and contact lists.
  • For Unicode names, charAt(0) works for BMP characters. For names starting with supplementary characters, use codePointAt(0).

What are common interview follow-up questions?

DIAGRAM
- "How do you group by the last letter?" Use charAt(name.length() - 1).
- "How do you group by name length?" Use e -> e.getName().length() as the classifier.
- "How do you find the most common initial?" Stream the grouped map and max by value size.

Visual Overview

DIAGRAM
  Stream<Employee>
  [Rajesh, Priya, Amit, Kavita, Ananya, Neha]
       │
       │  classifier: name → first letter (uppercase)
       ▼
  Mapping:
    Rajesh → 'R'
    Priya  → 'P'
    Amit   → 'A'
    Kavita → 'K'
    Ananya → 'A'
    Neha   → 'N'
       │
       │  groupingBy (TreeMap for order)
       ▼
  Map<Character, List<Employee>>
  ┌─────────────────────────────┐
  │  A → [Amit, Ananya]        │
  │  K → [Kavita]              │
  │  N → [Neha]                │
  │  P → [Priya]               │
  │  R → [Rajesh]              │
  └─────────────────────────────┘

Key Takeaway

To build a phone-book-style directory, pass a classifier to groupingBy that extracts the first character of each name. Use Character.toUpperCase to normalise the initial so that different cases land in the same bucket.

Always guard against null and empty names before calling charAt(0), because charAt on an empty string throws StringIndexOutOfBoundsException. Supply a TreeMap for alphabetical key order.

At the National Bank of India, this pattern powered the employee directory where users click a letter to see the matching employees. The classifier must be total and deliberate: guard against malformed input, decide case sensitivity up front, and choose the map type to match the presentation.

Remember one sentence: groupingBy with charAt(0) builds an alphabetical index, and always normalise case and guard against empty names.

---

Share
Use ← → arrow keys to navigate