Definition
comparingInt(Employee::getAge)extracts the primitive int age with no boxing.thenComparing(Employee::getName)resolves ties when ages are equal, using natural String order.- Performance:
comparingIntavoids autoboxing each age into anInteger. - Case sensitivity: natural String order is case-sensitive; add
CASE_INSENSITIVE_ORDERif needed. - Stability: identical records keep their original relative order.
Priya at the National Bank of India wants the staff birthday list sorted youngest to oldest, and when two colleagues are the same age she wants them in alphabetical order so the list is predictable.
Understanding the Concepts
How do you sort by age then by name?
package com.bank.hr;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
public class SortByAgeThenName {
public static void main(String[] args) {
List<Employee> staff = List.of(
new Employee("Rajesh", 35, "Loans", new BigDecimal("95000")),
new Employee("Priya", 30, "IT", new BigDecimal("120000")),
new Employee("Amit", 30, "Finance", new BigDecimal("85000")),
new Employee("Kavita", 28, "Loans", new BigDecimal("78000")));
List<Employee> sorted = staff.stream()
.sorted(Comparator.comparingInt(Employee::getAge)
.thenComparing(Employee::getName))
.collect(Collectors.toList());
sorted.forEach(e -> System.out.printf("%s (age %d)%n",
e.getName(), e.getAge()));
}
}
Kavita (age 28)
Amit (age 30)
Priya (age 30)
Rajesh (age 35)
Amit and Priya are both 30, so the name tie-breaker puts Amit before Priya.
Why use comparingInt instead of comparing?
| Method | Key type | Boxing? | Performance |
|---|---|---|---|
| comparingInt(Employee::getAge) | primitive int | No | Better |
| comparing(Employee::getAge) | Integer (boxed) | Yes | Slower on hot paths |
// Preferred: no boxing
Comparator.comparingInt(Employee::getAge)
// Avoid: autoboxes each int to Integer
Comparator.comparing(Employee::getAge)
What about case-insensitive name tie-breaking?
Comparator<Employee> comp = Comparator
.comparingInt(Employee::getAge)
.thenComparing(Employee::getName, String.CASE_INSENSITIVE_ORDER);
What are common mistakes?
Mistake: using comparing instead of comparingInt for a primitive int field.
// Less efficient: boxes every age
Comparator.comparing(Employee::getAge)
.thenComparing(Employee::getName);
// Better: no boxing
Comparator.comparingInt(Employee::getAge)
.thenComparing(Employee::getName);
What about descending age with ascending name?
Comparator<Employee> desc = Comparator
.comparingInt(Employee::getAge).reversed()
.thenComparing(Employee::getName);
What about three-level tie-breaking?
// Age ASC, then name ASC, then department ASC
Comparator<Employee> threeLevel = Comparator
.comparingInt(Employee::getAge)
.thenComparing(Employee::getName)
.thenComparing(Employee::getDepartment);
What should a senior engineer know?
comparingIntreturns a specialised comparator that avoids boxing.- The composed comparator is built once and applied to every pair, so the composition cost is negligible.
- Natural String order is case-sensitive; if names come from inconsistent imports, plug in
CASE_INSENSITIVE_ORDER. - sorted is stable, so employees identical on both age and name keep their input order.
What are common interview follow-up questions?
- "When is comparingInt better than comparing?" When the key is a primitive int, to avoid boxing.
- "How do you reverse only the age?" Call
.reversed()aftercomparingInt(age)beforethenComparing. - "Is the sort stable?" Yes, equal elements keep their input order.
Visual Overview
Before sort:
[Rajesh/35, Priya/30, Amit/30, Kavita/28]
│
│ sorted(comparingInt(age).thenComparing(name))
│
│ Primary: age ascending
│ Kavita(28) < Amit(30) = Priya(30) < Rajesh(35)
│
│ Tie-break: Amit vs Priya (both 30) → name ASC
│ "Amit" < "Priya" → Amit first
▼
[Kavita/28, Amit/30, Priya/30, Rajesh/35]
Key Takeaway
To sort by age and then by name I chain comparators: comparingInt(Employee::getAge) for the primary key, and thenComparing(Employee::getName) for the tie-breaker. I deliberately use comparingInt rather than comparing because age is a primitive int and comparingInt avoids boxing.
In my banking project at the National Bank of India this drove a wellness check-up roster that grouped staff youngest to oldest, with same-age employees listed alphabetically.
The sort is stable, so employees identical on both age and name keep their input order. If names come from inconsistent imports I plug in String.CASE_INSENSITIVE_ORDER as the tie-breaker comparator.
Remember one sentence: comparingInt(age).thenComparing(name) sorts by age without boxing, with alphabetical name as the deterministic tie-breaker.
---