How do you sort by multiple fields using Comparator? — Cracked Java
// Java Collections Framework · Comparable vs Comparator
MidCoding

How do you sort by multiple fields using Comparator?

Use Comparator.comparing(...) to extract a sort key, then chain thenComparing(...) for tiebreakers. Add .reversed() to flip direction, and use the primitive-specialized variants (comparingInt, comparingLong, comparingDouble) to avoid boxing on hot paths.

The pattern

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

// Sort by department asc, then salary desc, then name asc, then age asc
Comparator<Employee> cmp =
    Comparator.comparing(Employee::department)
              .thenComparing(Employee::salary, Comparator.reverseOrder())
              .thenComparing(Employee::name)
              .thenComparingInt(Employee::age);

employees.sort(cmp);

Read top to bottom — first key wins; subsequent keys break ties.

Watch out: .reversed() reverses everything before it

// BUG: reversed() flips the whole chain, not just salary
var bug = Comparator.comparing(Employee::department)
                    .thenComparing(Employee::salary)
                    .reversed();
// This sorts by department DESC, then salary DESC.

To reverse only one key, pass an explicit comparator:

// GOOD: only salary descends
var good = Comparator.comparing(Employee::department)
                     .thenComparing(Employee::salary, Comparator.reverseOrder())
                     .thenComparing(Employee::name);

Primitive specializations avoid boxing

// Boxing — each compare allocates an Integer
Comparator.comparing(Employee::age);

// No boxing — uses int directly
Comparator.comparingInt(Employee::age);

Comparator.comparing takes a Function<T, U extends Comparable<? super U>> — the return type must be a reference. Integer.compareTo boxes its arg. For hot sort paths over large collections, prefer comparingInt/comparingLong/comparingDouble.

Common chains

// Top-N by score
var topN = stream.sorted(Comparator.comparingDouble(Item::score).reversed())
                 .limit(n)
                 .toList();

// Group then sort each group
Map<String, List<Employee>> byDept = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::department,
        Collectors.collectingAndThen(
            Collectors.toList(),
            list -> { list.sort(Comparator.comparing(Employee::name)); return list; }
        )
    ));

// Sort stable by multiple keys (Collections.sort is stable since Java 7)
list.sort(Comparator.comparing(Item::category)
                    .thenComparing(Item::priority, Comparator.reverseOrder()));

Stability matters when chaining

Collections.sort and List.sort are stable — elements that compare equal keep their original relative order. This means a two-pass sort sort by name; sort by department; gives the same result as sort by department then name (the second sort preserves the name order within each department). Chained comparators are more readable; two-pass sorts work too.

Mark your status