Comparable vs Comparator in Java: Which Sorting Strategy to Use

When you need to sort objects in Java, there are two interfaces in the standard library: Comparable and Comparator. Both control sorting order, but they answer fundamentally different questions — and confusing them leads to rigid designs.

Comparable: natural ordering built into the class

Comparable<T> (in java.lang) is implemented by a class itself to declare one natural ordering. If you write class Student implements Comparable<Student>, you are saying: “this class has one default order, and here is how two instances compare.”

The contract is simple: compareTo returns a negative number if the current instance is less than the argument, zero if equal, and a positive number if greater.

class StudentComparable implements Comparable<StudentComparable> {
    String name;
    int age;
    double gpa;

    StudentComparable(String name, int age, double gpa) {
        this.name = name;
        this.age = age;
        this.gpa = gpa;
    }

    @Override
    public int compareTo(StudentComparable other) {
        return this.name.compareTo(other.name);
    }
}

The beauty of Comparable is that once implemented, any sorting utility — Collections.sort(), Arrays.sort(), even a TreeSet — automatically knows how to order instances:

In the output above, the three students appear in alphabetical order by name without any extra configuration. The sorting logic lives inside the class definition, which is both its strength and its limitation.

Comparator: external, composable ordering strategies

Comparator<T> (in java.util) lives outside the class. It doesn’t modify the type at all — instead, you hand a comparison function to the sort call:

class StudentComparator {
    String name;
    int age;
    double gpa;
    // ... no Comparable implementation ...
}

Now any number of orderings can be defined at the call site:

Two sorts on the same list, two completely different orderings, zero changes to the StudentComparator class. That flexibility is what makes Comparator indispensable.

Key differences

ComparableComparator
Where definedInside the class (implements)At call site (anonymous class, lambda, method reference)
How many orderingsExactly one per classMany per class
Packagejava.langjava.util
MethodcompareTo(T o)compare(T o1, T o2)
Impact on existing typesRequires modifying the sourceWorks with any type, even those you can’t modify

When to use which

  • Reach for Comparable when a class has one clear, canonical ordering (e.g., names alphabetically, dates chronologically) and it makes sense for that order to be the default everywhere.
  • Use Comparator when you need multiple orderings, want to sort types you can’t modify (from a library or third party), or need to compose/order by derived values (a substring, a computed field).

The mental model: Comparable answers “what is this object’s natural position?” — it’s built into the type. Comparator answers “how should I compare these two objects right now?” — it’s a contextual tool you plug in where needed.