Q19 of 40 · Core Java
What is the difference between Comparable and Comparator?
Short answer
Short answer: Comparable defines a class's natural ordering via compareTo() — the class itself implements it and can only have one. Comparator is an external comparison strategy passed to sort operations — you can define many and swap them at runtime. Comparable is 'the default sort'; Comparator is 'a custom sort'.
Detail
Comparable<T> is implemented by the class being compared. Its single method compareTo(T other) returns a negative int (this < other), zero (equal), or positive int (this > other). Standard library types like String, Integer, LocalDate, and BigDecimal implement Comparable. This enables Collections.sort(list) and TreeMap with no arguments — they use the natural order.
A class has only one compareTo implementation — you can't have both "sort users by name" and "sort users by ID" built in. That's where Comparator comes in.
Comparator<T> is an external strategy object. You can create as many as you need and compose them. Java 8 added fluent factory methods: Comparator.comparing(), thenComparing(), reversed(), nullsFirst(), nullsLast(). These make complex multi-field sorts readable.
compareTo contract: must be consistent with equals — if a.compareTo(b) == 0 then a.equals(b) should be true. Violating this causes bizarre behaviour with TreeSet and TreeMap (they use compareTo not equals for membership).
In test automation: sort test cases for reporting (Comparator.comparing(TestCase::severity).thenComparing(TestCase::id)), produce stable orderings for snapshot assertions, or use TreeMap with a custom comparator to group results.
// EXAMPLE
ComparableVsComparator.java
import java.util.Comparator;
import java.util.List;
// Comparable — natural ordering built into the class
record Severity(String name, int level) implements Comparable<Severity> {
@Override
public int compareTo(Severity other) {
return Integer.compare(this.level, other.level);
}
}
Severity critical = new Severity("CRITICAL", 4);
Severity minor = new Severity("MINOR", 1);
System.out.println(critical.compareTo(minor)); // positive — critical > minor
// Comparator — external, multiple strategies
record TestCase(String id, Severity severity, long durationMs) {}
// Sort by severity descending, then by duration ascending
Comparator<TestCase> reportOrder = Comparator
.comparing(TestCase::severity).reversed() // high severity first
.thenComparingLong(TestCase::durationMs); // shortest first within level
List<TestCase> cases = getTestCases();
cases.sort(reportOrder); // sorts in-place
var sorted = cases.stream()
.sorted(reportOrder) // stream version, returns new sorted stream
.toList();
// Null-safe comparator for optional fields
Comparator<TestCase> nullSafe = Comparator
.comparing(TestCase::id, Comparator.nullsLast(Comparator.naturalOrder()));