Skip to content

Lesson 05 · Comparison & Sorting

Objectives

After this lesson you will be able to:

  • Implement Comparable (natural order) and Comparator (custom order).
  • Build comparators with comparing, thenComparing, reversed, and null-handling.
  • Respect the equals/hashCode/compareTo consistency contracts.

Comparable vs Comparator

  • Comparable<T> — a type's natural ordering, via int compareTo(T o) on the class itself.
  • Comparator<T> — an external, swappable ordering, via int compare(T a, T b).

compareTo/compare return negative / zero / positive for less / equal / greater.

java
record Person(String name, int age) implements Comparable<Person> {
    public int compareTo(Person o) { return Integer.compare(age, o.age); }  // by age
}
list.sort(null);                                   // natural order (Comparable)
list.sort(Comparator.comparing(Person::name));     // by name instead

Exam trap

Don't compare with subtraction (a.age - b.age) — it overflows for large/negative values. Use Integer.compare(a, b). Collections.sort/List.sort need either Comparable elements or a Comparator, otherwise you get a ClassCastException (or it won't compile with a typed Comparator).

Building comparators

Comparator has fluent factories that chain:

java
Comparator<Person> byAgeThenName =
    Comparator.comparingInt(Person::age)
              .thenComparing(Person::name);

Comparator<Person> byNameDesc =
    Comparator.comparing(Person::name).reversed();

Comparator<Person> nullsLast =
    Comparator.comparing(Person::name, Comparator.nullsLast(Comparator.naturalOrder()));

The consistency contracts

  • equals/hashCode: equal objects must have equal hash codes (Module 03).
  • compareTo consistent with equals: ideally x.compareTo(y) == 0 iff x.equals(y).

Gotcha

A TreeSet/TreeMap decides equality by compareTo/compare returning 0, not equals. So a comparator that only compares one field treats two different-but-"equal-by-that-field" objects as duplicates — the second is dropped. Make the comparator a total order (add a tiebreaker) if you need all elements.

SDET note

When asserting sorted output, pin the order with an explicit, total Comparator (add a tiebreaker) so ties don't reorder between runs. For value objects, a record's generated equals plus a compareTo consistent with it keeps TreeSet/assertEquals behaving predictably.

Key Takeaways

  • Comparable = natural order (compareTo on the type); Comparator = external order (compare). Both return negative/zero/positive.
  • Compare numbers with Integer.compare, never subtraction (overflow).
  • Build comparators with comparing/comparingInt/thenComparing/reversed/nullsLast.
  • TreeSet/TreeMap use compareTo/compare == 0 for equality — use a total order or lose "duplicates".

Lesson Quiz

Lesson Quiz · Comparison & Sorting0 / 5
  1. Why avoid return a.age - b.age; in compareTo?

    • AIt's slower
    • BIt can overflow for large/negative values
    • CIt returns a boolean
    • DIt doesn't compile
  2. Comparable defines order via... and Comparator via...

    • Acompare / compareTo
    • BcompareTo / compare
    • Cequals / compare
    • Dsort / order
  3. How do you sort by age, then break ties by name?

    • Acomparing(Person::age).comparing(Person::name)
    • BcomparingInt(Person::age).thenComparing(Person::name)
    • Ccomparing(Person::name).reversed()
    • Dcomparing(Person::age, Person::name)
  4. A TreeSet uses a comparator that only compares the name field. Two people share a name. What happens?

    • ABoth are kept
    • BThe second is treated as a duplicate and dropped
    • CClassCastException
    • DThey are merged
  5. What does list.sort(null) do for elements implementing Comparable?

    • ANullPointerException
    • BSorts by natural order
    • CNo-op
    • DReverses the list

Next: Module 05 Mini-Exam. Run the matching code in labs/src/main/java/com/jse21/m05_collections/.