Comparable as an interface example in Java
Natural ordering via compareTo.
A built-in sense of order
Implement **Comparable<T> and your class gets a natural ordering** through compareTo. It returns a negative number if this comes first, zero if they tie, positive if this comes after. Collections.sort, TreeSet and TreeMap use it automatically.
class Player implements Comparable<Player> {
int score;
public int compareTo(Player o) {
return Integer.compare(score, o.score);
}
}Capital letters first?
String already implements Comparable. What prints?
List<String> w = new ArrayList<>(
List.of("kiwi", "Banana", "apple"));
Collections.sort(w);
System.out.println(w);[apple, Banana, kiwi][Banana, apple, kiwi][kiwi, Banana, apple]
Show the answer
String's natural order compares character codes. Uppercase 'B' is 66, lowercase 'a' is 97 — so "Banana" sorts before "apple".
Flip the order
Want highest first? Swap the arguments: Integer.compare(o.rank, rank). Now a Card with rank 3 compared to one with rank 8 returns a positive number, so the 3 sorts *after* the 8 — descending order.
public int compareTo(Card o) {
return Integer.compare(o.rank, rank);
}The subtraction shortcut
return price - o.price; looks clever but overflows for huge or very negative values: Integer.MIN_VALUE - 1 wraps to a large positive number and flips the order. Always use **Integer.compare(a, b)** — it can't overflow.
public int compareTo(Item o) {
return price - o.price; // risky!
}A TreeSet of strangers
Gem doesn't implement Comparable. What happens?
class Gem { }
void main() {
var set = new TreeSet<Gem>();
set.add(new Gem());
}Adds the gem fineThrows ClassCastExceptionCompile error
Show the answer
It compiles — TreeSet<Gem> accepts any type. But a TreeSet needs an order: with no Comparator it casts elements to Comparable, and that fails at runtime, even on the very first add.
Ordering in real apps
Leaderboards, orders by date, prices: String, Integer and LocalDate already implement Comparable, so they sort out of the box. Try to keep compareTo **consistent with equals** — otherwise a TreeSet and a HashSet can disagree about which items are duplicates.
Key takeaways
- int compareTo(T other): negative / zero / positive
- String, Integer and LocalDate already implement it
- Use Integer.compare(a, b), not a - b, to avoid overflow
- Keep compareTo consistent with equals where possible
new BigDecimal("2.0").equals(new BigDecimal("2.00")) is false, but compareTo returns 0. A HashSet keeps both values; a TreeSet keeps only one.
Practice questions
What does this print?
List<String> words =
new ArrayList<>(List.of("pear", "Apple", "fig"));
Collections.sort(words);
System.out.println(words);- [Apple, fig, pear]
- [fig, pear, Apple]
- [pear, Apple, fig]
- [Apple, pear, fig]
Check your answer
[Apple, fig, pear]. String's natural order compares character codes. 'A' (65) is smaller than 'f' (102) and 'p' (112).
What does this print?
class Card implements Comparable<Card> {
int rank;
Card(int rank) { this.rank = rank; }
public int compareTo(Card o) {
return Integer.compare(o.rank, rank);
}
}
void main() {
Card a = new Card(3), b = new Card(8);
System.out.println(a.compareTo(b) > 0);
}- true
- false
- Compile error
- Throws ClassCastException
Check your answer
true. The arguments are swapped (o.rank first), which reverses the order: Integer.compare(8, 3) is positive, so the 3 sorts after the 8.