Back to Stream: Sorting & Comparator
Q20Question 20 of 23

Sort employees by year of joining (most recent first)

Key Concepts
  • comparingInt(Employee::getYearOfJoining).reversed() puts the most recent joiners first
  • reversed() on comparingInt flips ascending to descending order
  • The order of calls matters: reverse the year first, then add a name tie-breaker
  • comparingInt avoids boxing the year into an Integer on every comparison
  • sorted() never mutates the source list; it produces a new ordered stream
  • For dates stored as LocalDate, use comparing(Employee::getJoinDate) with natural LocalDate order

Definition

`comparingInt(year).reversed()` puts the latest-joining employees at the top.
  • comparingInt(Employee::getYearOfJoining).reversed() flips ascending year to descending, most recent first.
  • Call ordering: reverse the year first, then thenComparing(name) stays ascending.
  • comparingInt avoids 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) uses LocalDate.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?

JAVA
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()));
    }
}
TEXT
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! |

JAVA
// 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.

JAVA
// 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?

JAVA
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?

JAVA
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?

  • comparingInt returns 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

DIAGRAM
  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.

---

Share
Use ← → arrow keys to navigate