Definition
comparingInt(Employee::getYearOfJoining).reversed()flips ascending year to descending, most recent first.- Call ordering: reverse the year first, then
thenComparing(name)stays ascending. comparingIntavoids boxing the int year on every comparison.- Non-mutating:
sorted()produces a new stream; the source list is unchanged. - LocalDate alternative: for exact dates,
comparing(Employee::getJoinDate)usesLocalDate.compareTo.
The National Bank of India wants a "newest joiners first" list for its onboarding review. The people who joined most recently should be at the top.
Understanding the Concepts
How do you sort most-recent-first?
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class SortByJoinYear {
public static void main(String[] args) {
List<Employee> staff = List.of(
new Employee("Rajesh", 2015),
new Employee("Priya", 2022),
new Employee("Amit", 2019),
new Employee("Kavita", 2022),
new Employee("Suresh", 2010));
List<Employee> sorted = staff.stream()
.sorted(Comparator.comparingInt(Employee::getYearOfJoining).reversed()
.thenComparing(Employee::getName))
.collect(Collectors.toList());
sorted.forEach(e -> System.out.printf("%s (joined %d)%n",
e.getName(), e.getYearOfJoining()));
}
}
Kavita (joined 2022)
Priya (joined 2022)
Amit (joined 2019)
Rajesh (joined 2015)
Suresh (joined 2010)
Kavita and Priya both joined in 2022, so the name tie-breaker puts Kavita before Priya.
Why does call ordering matter?
| Expression | Year order | Name order |
|---|---|---|
| comparingInt(year).reversed().thenComparing(name) | Descending | Ascending |
| comparingInt(year).thenComparing(name).reversed() | Descending | Also descending! |
// CORRECT: reverse year, then add name ascending
Comparator.comparingInt(Employee::getYearOfJoining).reversed()
.thenComparing(Employee::getName);
// WRONG: reverses everything, including the name
Comparator.comparingInt(Employee::getYearOfJoining)
.thenComparing(Employee::getName)
.reversed();
What are common mistakes?
Mistake: forgetting to add a tie-breaker, making the output non-deterministic for same-year joiners.
// Non-deterministic for same-year joiners
staff.stream()
.sorted(Comparator.comparingInt(Employee::getYearOfJoining).reversed())
.collect(Collectors.toList());
// Kavita and Priya could appear in either order!
// Deterministic with name tie-breaker
staff.stream()
.sorted(Comparator.comparingInt(Employee::getYearOfJoining).reversed()
.thenComparing(Employee::getName))
.collect(Collectors.toList());
What about sorting by LocalDate instead of year?
import java.time.LocalDate;
// If joinDate is a LocalDate, natural order is chronological
staff.stream()
.sorted(Comparator.comparing(Employee::getJoinDate).reversed())
.collect(Collectors.toList());
What about grouping by year after sorting?
import java.util.LinkedHashMap;
Map<Integer, List<Employee>> byYear = staff.stream()
.sorted(Comparator.comparingInt(Employee::getYearOfJoining).reversed())
.collect(Collectors.groupingBy(
Employee::getYearOfJoining,
LinkedHashMap::new,
Collectors.toList()));
What should a senior engineer know?
comparingIntreturns a specialised comparator;.reversed()works cleanly on it.- Always add a deterministic tie-breaker for reports that must be reproducible.
- The source list is never mutated.
- For parallel streams, sorted is a barrier that requires all elements before it can begin ordering.
What are common interview follow-up questions?
- "What if two employees joined in the same year?" Add a thenComparing tie-breaker.
- "Does reversed affect the tie-breaker?" Only if placed after the whole chain.
- "Can you sort by full join date?" Yes, use
Comparator.comparing(Employee::getJoinDate).
Visual Overview
Unsorted: [Rajesh/2015, Priya/2022, Amit/2019, Kavita/2022, Suresh/2010]
│
│ sorted(comparingInt(year).reversed().thenComparing(name))
│
│ Year DESC: 2022, 2022, 2019, 2015, 2010
│ Tie-break (2022): Kavita < Priya (name ASC)
▼
[Kavita/2022, Priya/2022, Amit/2019, Rajesh/2015, Suresh/2010]
Key Takeaway
To sort most-recent-first I use comparingInt(Employee::getYearOfJoining).reversed(), which flips ascending year to descending. The important detail is call ordering: I reverse the year comparator first and only then add the name tie-breaker with thenComparing, so the tie-breaker stays ascending.
In my banking project at the National Bank of India this was the onboarding review list, floating newest joiners to the top. I always add a deterministic tie-breaker so same-year joiners appear in a reproducible order.
Remember one sentence: comparingInt(year).reversed().thenComparing(name) sorts most-recent-first with an alphabetical tie-breaker, and the position of reversed() determines which keys are flipped.
---