Definition
.reversed()flips any comparator's ordering, turning ascending into descending.- Scope of reversal:
comparing(salary).reversed()reverses only the salary; a subsequentthenComparing(name)stays ascending. - Alternative:
Comparator.comparing(key, Comparator.reverseOrder())reverses a single key inline. - Non-mutating:
sortedproduces a new stream; the source list is never changed. - Stability: reversed sorts are still stable; equal salaries keep their original relative order.
The payroll manager at the National Bank of India now wants the opposite order: the highest earners at the top, like lining people up tallest to shortest. Take the same salary comparator and flip it with reversed().
Understanding the Concepts
How do you sort descending by salary?
package com.bank.hr;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
public class SortDescending {
public static void main(String[] args) {
List<Employee> staff = List.of(
new Employee("Rajesh", "Loans", new BigDecimal("95000")),
new Employee("Priya", "IT", new BigDecimal("120000")),
new Employee("Amit", "Finance", new BigDecimal("85000")),
new Employee("Kavita", "Loans", new BigDecimal("78000")));
List<Employee> descending = staff.stream()
.sorted(Comparator.comparing(Employee::getSalary).reversed())
.collect(Collectors.toList());
descending.forEach(e -> System.out.printf("%s - Rs. %s%n",
e.getName(), e.getSalary().toPlainString()));
}
}
Priya - Rs. 120000
Rajesh - Rs. 95000
Amit - Rs. 85000
Kavita - Rs. 78000
Where does reversed() bind in a chain?
This is a frequent source of subtle bugs. Be deliberate about where reversed binds.
| Expression | Salary order | Name tie-breaker order |
|---|---|---|
| comparing(salary).reversed().thenComparing(name) | Descending | Ascending |
| comparing(salary).thenComparing(name).reversed() | Descending | Also descending (both flipped!) |
// CORRECT: reverse only salary, keep name ascending
Comparator<Employee> correct = Comparator
.comparing(Employee::getSalary).reversed()
.thenComparing(Employee::getName);
// WRONG: reverses the whole chain, including the name
Comparator<Employee> wrong = Comparator
.comparing(Employee::getSalary)
.thenComparing(Employee::getName)
.reversed(); // both salary AND name are now reversed!
What are common mistakes?
Mistake: reversing the entire composed comparator instead of just the primary key.
// WRONG: this reverses both salary and name
staff.stream()
.sorted(Comparator.comparing(Employee::getSalary)
.thenComparing(Employee::getName)
.reversed()) // flips everything!
.collect(Collectors.toList());
// CORRECT: reverse only the salary
staff.stream()
.sorted(Comparator.comparing(Employee::getSalary).reversed()
.thenComparing(Employee::getName))
.collect(Collectors.toList());
What is the alternative reverseOrder approach?
// Using Comparator.reverseOrder() as the key comparator
Comparator<Employee> alt = Comparator.comparing(
Employee::getSalary, Comparator.reverseOrder());
This is equivalent to comparing(salary).reversed() but some developers find it clearer because the reversal is stated inline.
What about using Comparator.reversed with thenComparing?
// Highest salary first; among equal salaries, alphabetical name
List<Employee> result = staff.stream()
.sorted(Comparator.comparing(Employee::getSalary, Comparator.reverseOrder())
.thenComparing(Employee::getName))
.collect(Collectors.toList());
What should a senior engineer know?
reversed()is a decorator: it wraps the original comparator and negates its result.- The sort remains stable after reversal; equal elements keep their relative order.
- The source list is never mutated.
- Precision about where
reversed()binds is what separates correct multi-key sorts from subtly broken ones.
What are common interview follow-up questions?
- "What does reversed do internally?" It wraps the comparator and negates the comparison result.
- "Does reversed affect thenComparing?" Only if reversed is called after the whole chain.
- "Is the reversed sort still stable?" Yes.
Visual Overview
Unsorted: [Rajesh/95000, Priya/120000, Amit/85000, Kavita/78000]
│
│ sorted(comparing(salary).reversed())
▼
Sorted descending: [Priya/120000, Rajesh/95000, Amit/85000, Kavita/78000]
reversed() binding:
comparing(salary).reversed().thenComparing(name)
│ │ │
│ salary DESC │ name ASC │
└──────────────────┴──────────────┘
Key Takeaway
To sort descending I build the ascending comparator with Comparator.comparing and call .reversed(), which returns a new comparator that negates every comparison result so the highest salary comes first.
The critical detail is where reversed() binds in a chain. If I write comparing(salary).reversed().thenComparing(name), reversed applies only to salary and the name tie-breaker stays ascending. If instead I reversed the whole composed comparator, the name would flip too, which is a subtle and common bug.
In my banking project at the National Bank of India this was a top-earners view for a compensation audit. The sort is still stable, non-mutating, and for BigDecimal the comparison is exact.
Remember one sentence: comparing(salary).reversed() gives descending salary, and the position of .reversed() in a chain determines exactly which keys are flipped.
---