Comparable vs Comparator in Java
Natural ordering vs external ordering; Comparator.comparing().thenComparing().reversed().
Built-in order vs plug-in order
**Comparable gives a class its natural ordering** from the inside, via compareTo(other). **Comparator defines an ordering from outside**, via compare(a, b), so one type can have many orders. Both return negative, zero or positive.
// inside the class: one natural order
record Ver(int n) implements Comparable<Ver> {
public int compareTo(Ver o) {
return Integer.compare(n, o.n);
}
}Who uses natural order?
Collections.sort, List.sort(null), **TreeSet and TreeMap** fall back to compareTo when you don't supply a Comparator. String, Integer and LocalDate all implement Comparable. For any other order, build a Comparator like Lego: **comparing(key), then thenComparing(key2)** to break ties.
people.sort(Comparator
.comparing(Person::city)
.thenComparing(Person::name));Your turn
Sort by age, ties broken by name. What prints?
record P(String name, int age) {}
void main() {
var ps = new ArrayList<>(List.of(
new P("Zed", 20), new P("Amy", 30),
new P("Bea", 20)));
ps.sort(Comparator.comparingInt(P::age)
.thenComparing(P::name));
for (P p : ps) IO.print(p.name() + " ");
}Zed Amy BeaBea Zed AmyAmy Bea Zed
Show the answer
Age first: the two 20-year-olds come before Amy (30). Between them, thenComparing(P::name) puts Bea before Zed.
reversed() flips everything before it
**reversed() wraps the whole chain built so far**, not just the last key. To reverse only one key, give that key its own order: thenComparing(key, Comparator.reverseOrder()).
comparing(P::age).thenComparing(P::name)
.reversed(); // age AND name reversed
comparing(P::age).thenComparing(P::name,
Comparator.reverseOrder()); // name onlyFlip it
Same people, now with .reversed() at the end. What prints?
record P(String name, int age) {}
void main() {
var ps = new ArrayList<>(List.of(
new P("Zed", 20), new P("Amy", 30),
new P("Bea", 20)));
ps.sort(Comparator.comparingInt(P::age)
.thenComparing(P::name).reversed());
for (P p : ps) IO.print(p.name() + " ");
}Amy Bea ZedAmy Zed BeaBea Zed Amy
Show the answer
Both keys flip: oldest first (Amy), then the 20-year-olds by name in reverse: Zed before Bea.
Comparing ints
public int compareTo(Temp o) {
return v - o.v;
}With v = Integer.MIN_VALUE and o.v = 1, the subtraction wraps to a huge positive number: the wrong sign.
public int compareTo(Temp o) {
return Integer.compare(v, o.v);
}Integer.compare returns -1, 0 or 1 and never overflows.
In real code
Every sortable table in an app, every "sort by price, then rating" button, every TreeMap key is a Comparator decision. A broken comparator can even crash a sort: Java's sort may throw IllegalArgumentException: Comparison method violates its general contract! when the results are inconsistent.
Key takeaways
- compareTo / compare return negative, zero or positive
- Comparable: one built-in order (String, Integer, LocalDate…)
- Comparator: any number of external orders
- reversed() reverses everything before it in the chain
💡 Comparable is a person's height written on their ID; a Comparator is a judge who can rank people by height, age or name.
Java sorts objects with TimSort, an algorithm Tim Peters invented for Python in 2002. Java adopted it for object sorting in Java 7.
Practice questions
What does this print?
record P(String name, int age) {}
void main() {
var ps = new ArrayList<>(List.of(new P("Bo", 30),
new P("Cy", 25), new P("Al", 25)));
ps.sort(Comparator.comparingInt(P::age)
.thenComparing(P::name));
ps.forEach(p -> System.out.print(p.name() + " "));
}- Bo Cy Al
- Al Cy Bo
- Al Bo Cy
- Cy Al Bo
Check your answer
Al Cy Bo. First by age: the two 25-year-olds come before Bo (30). The tie between Cy and Al is broken by name, so Al comes first.
What does this print?
record P(String name, int age) {}
void main() {
var ps = new ArrayList<>(List.of(new P("Bo", 30),
new P("Cy", 25), new P("Al", 25)));
ps.sort(Comparator.comparingInt(P::age)
.thenComparing(P::name).reversed());
ps.forEach(p -> System.out.print(p.name() + " "));
}- Bo Al Cy
- Bo Cy Al
- Al Cy Bo
- Cy Al Bo
Check your answer
Bo Cy Al. reversed() applies to the entire comparator built so far — both age AND name are reversed. Oldest first (Bo), then names in reverse: Cy before Al.