Definition
- Multi-key sort:
Comparator.comparing(primary).thenComparing(secondary)applies a secondary rule when the primary is equal. - Selective reversal: reverse only the salary sub-comparator, not the department.
thenComparing(key, Comparator.reverseOrder())is the cleanest inline reversal.- Stable sort: equal-key employees keep their original relative order.
- Non-mutating: the source list is unchanged.
At the National Bank of India, Priya from HR wants the staff register grouped by department in alphabetical order, and inside each department the highest-paid person should appear first. Two rules, applied in order.
Understanding the Concepts
How do you build a multi-key comparator?
package com.bank.hr;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
public class MultiKeySort {
public static void main(String[] args) {
List<Employee> staff = List.of(
new Employee("Rajesh", "Loans", new BigDecimal("95000")),
new Employee("Kavita", "Loans", new BigDecimal("78000")),
new Employee("Priya", "IT", new BigDecimal("120000")),
new Employee("Suresh", "IT", new BigDecimal("110000")),
new Employee("Amit", "Finance", new BigDecimal("85000")));
List<Employee> sorted = staff.stream()
.sorted(Comparator.comparing(Employee::getDepartment)
.thenComparing(Employee::getSalary, Comparator.reverseOrder()))
.collect(Collectors.toList());
sorted.forEach(e -> System.out.printf("%-8s %-10s Rs. %s%n",
e.getDepartment(), e.getName(), e.getSalary().toPlainString()));
}
}
Finance Amit Rs. 85000
IT Priya Rs. 120000
IT Suresh Rs. 110000
Loans Rajesh Rs. 95000
Loans Kavita Rs. 78000
How does thenComparing work?
thenComparing adds a second rule consulted only when the first rule says two elements are equal. For employees in the same department (primary key equal), the salary comparison (secondary key) decides their relative order.
| Comparison | Department equal? | Result decided by |
|---|---|---|
| Amit vs Priya | Finance != IT | Department (Finance first) |
| Priya vs Suresh | IT == IT | Salary (120000 > 110000: Priya first) |
| Rajesh vs Kavita | Loans == Loans | Salary (95000 > 78000: Rajesh first) |
What are common mistakes?
Mistake: reversing the entire chain instead of just the salary.
// WRONG: reverses both department AND salary
Comparator.comparing(Employee::getDepartment)
.thenComparing(Employee::getSalary)
.reversed(); // Loans before IT, and low salary first!
// CORRECT: reverse only the salary
Comparator.comparing(Employee::getDepartment)
.thenComparing(Employee::getSalary, Comparator.reverseOrder());
What about three-key sorting?
// Department ASC, then salary DESC, then name ASC
Comparator<Employee> threeKey = Comparator
.comparing(Employee::getDepartment)
.thenComparing(Employee::getSalary, Comparator.reverseOrder())
.thenComparing(Employee::getName);
What about using a separate Comparator variable?
For readability, extract the comparator into a named variable.
Comparator<Employee> byDeptThenSalary = Comparator
.comparing(Employee::getDepartment)
.thenComparing(Employee::getSalary, Comparator.reverseOrder());
List<Employee> sorted = staff.stream()
.sorted(byDeptThenSalary)
.collect(Collectors.toList());
What should a senior engineer know?
sortedis stable, so employees who tie on both keys keep their original relative order.sortedreturns a brand new stream and never mutates the source list.- The Comparator chain is built once and reused for every pair comparison during the sort.
- For very large lists, the O(n log n) sort with buffering dominates the stream pipeline cost.
What are common interview follow-up questions?
- "How do you add a third sort key?" Chain another
.thenComparing(). - "Does the position of reversed() matter?" Yes, it affects which keys are reversed.
- "Is the sort stable?" Yes, equal elements keep their input order.
Visual Overview
Comparator chain: ┌──────────────────────────────────────────────┐ │ 1. comparing(department) → ASC │ │ 2. thenComparing(salary, reverseOrder) → DESC│ └──────────────────────────────────────────────┘ Result: Finance Amit 85000 IT Priya 120000 ← salary DESC within IT IT Suresh 110000 Loans Rajesh 95000 ← salary DESC within Loans Loans Kavita 78000
Key Takeaway
A multi-key sort chains comparators. I start with Comparator.comparing for the primary key (department ascending) and add thenComparing for the secondary key (salary descending). The critical detail is scoping the reverse: I reversed only the salary sub-comparator using Comparator.reverseOrder(), not the whole chain, which would have flipped the department order too.
In my banking project at the National Bank of India this was the monthly compensation register. HR needed departments grouped alphabetically and, inside each department, the highest earners first.
sorted is stable, so employees who tie on both keys keep their original order, and it never mutates the source list.
Remember one sentence: comparing(dept).thenComparing(salary, reverseOrder()) sorts by department ascending then salary descending, and the reversal is scoped only to the salary key.
---