javabeginner

Comparator Chains — Multi-Field Sorting

Sort objects by multiple fields using Comparator.comparing, thenComparing, and custom comparators.

java
import java.util.*;
import java.util.stream.Collectors;

record Employee(String name, String dept, double salary, int yearsExp) {}

public class ComparatorChain {
    public static void main(String[] args) {
        var employees = new ArrayList<>(List.of(
            new Employee("Alice", "Engineering", 120000, 8),
            new Employee("Bob", "Engineering", 110000, 5),
            new Employee("Carol", "Sales", 95000, 10),
            new Employee("Dave", "Engineering", 120000, 12),
            new Employee("Eve", "Sales", 105000, 3)
        ));

        // Sort by department, then salary descending, then name
        var sorted = employees.stream()
            .sorted(
                Comparator.comparing(Employee::dept)
                    .thenComparing(Employee::salary, Comparator.reverseOrder())
                    .thenComparing(Employee::name)
            )
            .toList();

        sorted.forEach(e -> System.out.printf("%-8s %-12s $%,.0f  %dy%n",
            e.name(), e.dept(), e.salary(), e.yearsExp()));

        // Null-safe comparator
        Comparator<Employee> nullSafe = Comparator.comparing(
            Employee::dept, Comparator.nullsLast(Comparator.naturalOrder()));

        // Custom comparator with logic
        Comparator<Employee> byExperience = (a, b) -> {
            // Senior (10+) always first, then by salary
            boolean aSenior = a.yearsExp() >= 10;
            boolean bSenior = b.yearsExp() >= 10;
            if (aSenior != bSenior) return aSenior ? -1 : 1;
            return Double.compare(b.salary(), a.salary());
        };

        employees.sort(byExperience);

        // Min/Max
        Employee highest = employees.stream()
            .max(Comparator.comparing(Employee::salary))
            .orElseThrow();
        System.out.println("Highest paid: " + highest.name());

        // Group and sort within groups
        employees.stream()
            .collect(Collectors.groupingBy(Employee::dept))
            .forEach((dept, emps) -> {
                System.out.println("\n" + dept + ":");
                emps.stream()
                    .sorted(Comparator.comparing(Employee::salary).reversed())
                    .forEach(e -> System.out.println("  " + e.name() + " $" + e.salary()));
            });
    }
}

Use Cases

  • Multi-field sorting for display and reporting
  • Custom business logic ordering
  • Ranked and prioritized list generation

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.