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 this comes before other
  • Zero if they're considered equal in ordering
  • Positive int if this comes after other

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:

  1. Antisymmetric: a.compareTo(b) and b.compareTo(a) have opposite signs.
  2. Transitive: if a < b and b < c, then a < c.
  3. 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) and list.sort(null)
  • Arrays.sort(array)
  • TreeSet, TreeMap β€” sorted collections
  • PriorityQueue β€” 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 Comparable only if there's one obvious natural order. Otherwise, expose Comparators.

Master compareTo and you've mastered how Java orders everything.