Skip to content
Dev Dump

Comparator vs Comparable

Java has two mechanisms for comparing objects. Comparable defines a single natural order inside the class. Comparator defines custom ordering outside the class — you can have as many as you need.

ComparableComparator
WhereInside the classExternal / standalone
MethodcompareTo(T other)compare(T o1, T o2)
Packagejava.langjava.util
OrdersOne (natural)Many (custom)
Modify class?YesNo
UsageCollections.sort(list)Collections.sort(list, comp)

Implement Comparable<T> when your class has one obvious default ordering.

public class Person implements Comparable<Person> {
private String name;
private int age;
@Override
public int compareTo(Person other) {
return Integer.compare(this.age, other.age);
}
}

The return value contract:

  • Negativethis comes before other
  • Zero → equal
  • Positivethis comes after other
Collections.sort(people); // uses compareTo
people.sort(null); // same thing — null means natural order
TreeSet<Person> sorted = new TreeSet<>(people); // also uses compareTo

Use when you need multiple sort orders or can’t modify the class.

public class NameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
}
people.sort(new NameComparator());
people.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));
people.sort(Comparator.comparing(Person::getName));
people.sort(Comparator.comparing(Person::getAge).reversed());

Sort by multiple fields with thenComparing:

Comparator<Person> comp = Comparator
.comparing(Person::getAge) // primary: age asc
.thenComparing(Person::getSalary, Comparator.reverseOrder()) // secondary: salary desc
.thenComparing(Person::getName); // tertiary: name asc
people.sort(comp);
// Nulls first
people.sort(Comparator.comparing(Person::getName, Comparator.nullsFirst(String::compareTo)));
// Nulls last
people.sort(Comparator.comparing(Person::getName, Comparator.nullsLast(String::compareTo)));

Use Comparable when:

  • The class has one obvious natural order (e.g., String alphabetically, Integer numerically)
  • You own the class and can modify it
  • You want TreeSet/TreeMap to work with your objects by default

Use Comparator when:

  • You need multiple different sort orders
  • You can’t modify the class (third-party code)
  • The ordering is context-dependent or temporary

1. Inconsistent multi-field comparison

// WRONG — fragile and error-prone
return (this.age + this.salary) - (other.age + other.salary);
// RIGHT — compare field by field
int cmp = Integer.compare(this.age, other.age);
if (cmp != 0) return cmp;
return Double.compare(this.salary, other.salary);
// BEST — use Comparator.comparing with chaining (shown above)

2. NullPointerException on fields

// WRONG
return this.name.compareTo(other.name); // NPE if name is null
// RIGHT — use null-safe comparators or guard manually
return Comparator.comparing(Person::getName, Comparator.nullsFirst(String::compareTo))
.compare(this, other);

3. Forgetting consistency with equals

If a.compareTo(b) == 0, then ideally a.equals(b) should also be true. TreeSet and TreeMap rely on compareTo for equality checks, not equals.