Back to Comparable & Comparator
Q3Question 3 of 4

How do you implement a Comparator to sort employees?

Key Concepts
  • Comparator.comparing() creates a comparator from a key extractor function; comparingInt/Long/Double avoid autoboxing
  • thenComparing() chains a secondary sort for tie-breaking; it composes cleanly for multi-level ordering
  • reversed() flips the order to descending; it can be applied to the entire chain or to individual levels
  • nullsFirst() and nullsLast() handle null values safely in the compared fields
  • Prefer factory methods over hand-written compare blocks for readability, null safety, and composability
  • Multi-field sorting pattern: Comparator.comparing(primary).thenComparing(secondary).thenComparing(tertiary)

Definition

Build Comparators using Comparator.comparing() for the primary sort and thenComparing() for tie-breaking, with reversed() for descending and nullsFirst/Last for null safety.
  • Comparator.comparing(keyExtractor): creates a Comparator from a function that extracts the sort key
  • comparingInt/Long/Double: primitive-specific versions that avoid autoboxing overhead
  • thenComparing(keyExtractor): adds a secondary sort applied when the primary comparison returns 0
  • reversed(): reverses the entire Comparator chain or a single level
  • nullsFirst(comparator) / nullsLast(comparator): wraps a Comparator to handle null values by placing them first or last

The bank manager writes a sorting rule: "Line customers by account balance (highest first). If two have the same balance, sort by name alphabetically. If names also match, sort by account opening date." Each level is a thenComparing call in the chain.

Understanding the Concepts

How do you implement a Comparator to sort by multiple fields?

Java 8 Comparator factory methods make multi-field sorting clean and readable.

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;

public class MultiFieldSortDemo {
    record Employee(String name, int age, String department, BigDecimal salary) {}

    public static void main(String[] args) {
        List<Employee> staff = new ArrayList<>(List.of(
            new Employee("Rajesh", 35, "IT", new BigDecimal("120000")),
            new Employee("Priya", 30, "Finance", new BigDecimal("95000")),
            new Employee("Amit", 30, "IT", new BigDecimal("110000")),
            new Employee("Sneha", 28, "Finance", new BigDecimal("95000")),
            new Employee("Vikram", 35, "Operations", new BigDecimal("105000"))
        ));

        // Sort by age, then by name
        staff.sort(Comparator.comparingInt(Employee::age)
                             .thenComparing(Employee::name));
        System.out.println("By age, then name:");
        staff.forEach(e -> System.out.println("  " + e.name() + " (" + e.age() + ")"));

        // Sort by salary descending, then by name ascending
        staff.sort(Comparator.comparing(Employee::salary).reversed()
                             .thenComparing(Employee::name));
        System.out.println("\nBy salary desc, then name:");
        staff.forEach(e -> System.out.println("  " + e.name() + ": " + e.salary()));

        // Sort by department, then by salary descending within each department
        staff.sort(Comparator.comparing(Employee::department)
                             .thenComparing(Employee::salary, Comparator.reverseOrder()));
        System.out.println("\nBy department, then salary desc:");
        staff.forEach(e -> System.out.println("  " + e.department() + " | " + e.name() + ": " + e.salary()));
    }
}
TEXT
By age, then name:
  Sneha (28)
  Amit (30)
  Priya (30)
  Rajesh (35)
  Vikram (35)

By salary desc, then name:
  Rajesh: 120000
  Amit: 110000
  Vikram: 105000
  Priya: 95000
  Sneha: 95000

By department, then salary desc:
  Finance | Priya: 95000
  Finance | Sneha: 95000
  IT | Rajesh: 120000
  IT | Amit: 110000
  Operations | Vikram: 105000

How do you handle nulls in sorting?

JAVA
package com.bank.demo;

import java.util.*;

public class NullSafeSortDemo {
    record Account(String holder, Integer priority) {}

    public static void main(String[] args) {
        List<Account> accounts = new ArrayList<>(List.of(
            new Account("Rajesh", 1),
            new Account(null, 2),     // null holder
            new Account("Priya", null), // null priority
            new Account("Amit", 3)
        ));

        // nullsFirst: null holders sort before non-null
        accounts.sort(Comparator.comparing(
            Account::holder,
            Comparator.nullsFirst(Comparator.naturalOrder())
        ));
        System.out.println("Null holders first:");
        accounts.forEach(a -> System.out.println("  " + a.holder()));
    }
}
TEXT
Null holders first:
  null
  Amit
  Priya
  Rajesh

What are the old-style vs new-style patterns?

JAVA
// OLD STYLE (pre-Java 8): anonymous class
Comparator<Employee> oldStyle = new Comparator<Employee>() {
    @Override
    public int compare(Employee a, Employee b) {
        int cmp = Integer.compare(a.age(), b.age());
        if (cmp != 0) return cmp;
        return a.name().compareTo(b.name());
    }
};

// NEW STYLE (Java 8+): factory methods + chaining
Comparator<Employee> newStyle = Comparator.comparingInt(Employee::age)
                                          .thenComparing(Employee::name);

// Both produce the same result, but the new style is:
// - More readable (reads like English)
// - Null-safe (with nullsFirst/nullsLast)
// - Composable (thenComparing, reversed)
// - Less error-prone (no manual Integer.compare / subtraction)

What are common mistakes?

Mistake: Using subtraction instead of Integer.compare

JAVA
// WRONG: integer overflow for large values
(a, b) -> a.age() - b.age() // if age is Integer.MIN_VALUE and b is positive: overflow!

// CORRECT: use Integer.compare
(a, b) -> Integer.compare(a.age(), b.age())

// BEST: use comparingInt
Comparator.comparingInt(Employee::age)

What should a senior engineer know?

- comparingInt avoids autoboxing. Comparator.comparing(Employee::getAge) boxes int to Integer for comparison. Comparator.comparingInt(Employee::getAge) uses primitive comparison directly – faster for large sorts.

- Comparator is a functional interface. You can use it as a lambda: (a, b) -> a.name().compareTo(b.name()). But factory methods are preferred for readability and safety.

- Stable sorting. Java's sort (TimSort) is stable: equal elements keep their relative order. This means sorting by department first, then sorting by name, gives the same result as a single sort by department-then-name.

What are common interview follow-up questions?

  • "How do you sort by multiple fields?" – Comparator.comparing().thenComparing().
  • "How do you sort in descending order?" – .reversed().
  • "How do you handle nulls in sorting?" – nullsFirst() or nullsLast().
  • "What is the difference between comparing() and comparingInt()?" – comparingInt avoids autoboxing.

Visual Overview

DIAGRAM
  Multi-field Comparator chaining

  Comparator.comparingInt(Employee::age)          ← primary: age asc
            .thenComparing(Employee::name)         ← secondary: name asc
            .thenComparing(Employee::salary,       ← tertiary: salary desc
                           Comparator.reverseOrder())

  Input:  Rajesh(35), Priya(30), Amit(30)
  Step 1: age: Priya(30), Amit(30), Rajesh(35)
  Step 2: tie on age=30: Amit, Priya (alphabetical)
  Result: Amit(30), Priya(30), Rajesh(35)

Key Takeaway

Java 8 Comparator factory methods make multi-field sorting clean and composable. Use comparing() for the primary sort, thenComparing() for tie-breaking, reversed() for descending order, and nullsFirst()/nullsLast() for null safety.

In the National Bank of India's HR reporting system, Rajesh sorts employees by department, then by salary descending within each department, using a single chained Comparator. The factory methods are more readable, more type-safe, and less error-prone than hand-written compare blocks.

Remember one sentence: Comparator.comparing().thenComparing().reversed() gives you multi-field, null-safe sorting in one readable chain.

---

Share
Use ← → arrow keys to navigate