Definition
- e.getName().charAt(0) extracts the first character as the grouping key.
- The result is
Mapwhere 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.
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(", "))));
}
}
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.
// 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.
// 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?
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));
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?
- "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
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.
---