compareTo() in Java: Ordering and Sorting
compareTo() is the method Java uses to define the natural order of objects. Any class that implements Comparable<T> can be sorted, used in TreeSet/TreeMap, or ordered via Collections.sort.
The contract
public interface Comparable<T> {
int compareTo(T other);
}
It returns:
- Negative int if
thiscomes beforeother - Zero if they're considered equal in ordering
- Positive int if
thiscomes afterother
The exact magnitudes don't matter β only the sign.
Built-in examples
"apple".compareTo("banana"); // negative β "apple" comes first
"banana".compareTo("apple"); // positive
"apple".compareTo("apple"); // 0
Integer.valueOf(3).compareTo(5); // negative
LocalDate.of(2026, 1, 1).compareTo(LocalDate.of(2026, 1, 2)); // negative
All standard types that have a natural ordering β numbers, strings, dates, chars β implement Comparable.
Implementing compareTo on your own class
public class User implements Comparable<User> {
private final String name;
private final int age;
public User(String name, int age) { this.name = name; this.age = age; }
@Override
public int compareTo(User other) {
return Integer.compare(this.age, other.age);
}
}
List<User> users = new ArrayList<>(List.of(
new User("Alice", 30),
new User("Bob", 25),
new User("Carol", 40)
));
Collections.sort(users);
// Bob(25), Alice(30), Carol(40)
Multi-field comparison
For "sort by age, then by name", chain comparators or use Comparator.comparing:
@Override
public int compareTo(User other) {
int byAge = Integer.compare(this.age, other.age);
if (byAge != 0) return byAge;
return this.name.compareTo(other.name);
}
Or more elegantly with Comparator (Java 8+):
Comparator<User> byAgeThenName = Comparator
.comparingInt(User::getAge)
.thenComparing(User::getName);
users.sort(byAgeThenName);
The contract rules
Your implementation must obey three rules or sorting and collections will break unpredictably:
- Antisymmetric:
a.compareTo(b)andb.compareTo(a)have opposite signs. - Transitive: if
a < bandb < c, thena < c. - Consistent: same pair, same result (no randomness, no external state).
Strongly recommended (but not required): (a.compareTo(b) == 0) == a.equals(b) β "consistent with equals". Violating this makes TreeSet behave differently from HashSet.
Common pitfalls
Integer overflow
// β Tempting but wrong β subtraction can overflow
return this.age - other.age;
// β
Use Integer.compare β no overflow, signals the correct sign
return Integer.compare(this.age, other.age);
null handling
compareTo(null) must throw NullPointerException per the Javadoc contract β don't return a sentinel value:
public int compareTo(User other) {
if (other == null) throw new NullPointerException();
return Integer.compare(this.age, other.age);
}
If you want nulls to sort first or last in a list, use Comparator.nullsFirst:
Comparator<User> c = Comparator.nullsFirst(Comparator.naturalOrder());
compareTo vs equals
They're related but not the same. equals tests object equality; compareTo tests ordering.
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("1.00");
a.equals(b); // false β different scale
a.compareTo(b); // 0 β same value
This is the canonical example of "not consistent with equals" β be aware of it when using BigDecimal in sets or maps.
Reverse order
users.sort(Comparator.reverseOrder()); // natural order reversed
users.sort(Comparator.comparingInt(User::getAge).reversed());
compareTo with Strings
String.compareTo does a lexicographic code-unit comparison β case-sensitive.
"Apple".compareTo("banana"); // negative β uppercase 'A' (65) < 'b' (98)
"Apple".compareToIgnoreCase("apple"); // 0
Where compareTo is used
Collections.sort(list)andlist.sort(null)Arrays.sort(array)TreeSet,TreeMapβ sorted collectionsPriorityQueueβ heap ordering- Stream terminal operations:
.sorted(),.min(),.max()
Summary
- Return a sign, not a magnitude.
- Use
Integer.compare,Long.compare,Double.compareβ never subtraction. - Respect antisymmetry, transitivity, and consistency.
- For complex ordering, prefer
Comparator.comparing(...).thenComparing(...). - Implement
Comparableonly if there's one obvious natural order. Otherwise, exposeComparators.
Master compareTo and you've mastered how Java orders everything.