Java provides two mechanisms for ordering objects: Comparable (natural ordering) and Comparator (custom ordering).

Comparable — Natural Ordering

Implement Comparable<T> to define the default sort order:

  public class Person implements Comparable<Person> {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Person other) {
        return this.name.compareTo(other.name); // sort by name
    }
}

List<Person> people = new ArrayList<>(List.of(
    new Person("Charlie", 30),
    new Person("Alice", 25),
    new Person("Bob", 35)
));

Collections.sort(people); // uses compareTo — Alice, Bob, Charlie
  

compareTo Contract

Must be consistent with equals:

  • Returns negative if this < other
  • Returns zero if equal
  • Returns positive if this > other

Comparator — Custom Ordering

Use Comparator when you need multiple sort orders or cannot modify the class:

  // Sort by age
Comparator<Person> byAge = Comparator.comparingInt(Person::age);
people.sort(byAge);

// Sort by name, then by age
Comparator<Person> byNameThenAge = Comparator
    .comparing(Person::name)
    .thenComparingInt(Person::age);

// Reverse order
people.sort(byAge.reversed());
  

Comparator Factory Methods

  Comparator<String> byLength = Comparator.comparingInt(String::length);
Comparator<String> natural = Comparator.naturalOrder();
Comparator<String> reverse = Comparator.reverseOrder();

// Null-safe
Comparator<String> nullSafe = Comparator.nullsFirst(Comparator.naturalOrder());
Comparator<String> nullsLast = Comparator.nullsLast(Comparator.naturalOrder());
  

Comparable vs Comparator

Feature Comparable Comparator
Package java.lang java.util
Method compareTo(T) compare(T, T)
Location Inside the class Separate class or lambda
Sort orders One (natural) Multiple
Modifies class Yes No

Sorting Collections and Arrays

  // List
List<String> names = new ArrayList<>(List.of("Charlie", "Alice", "Bob"));
names.sort(String::compareToIgnoreCase);

// Array
String[] arr = {"Charlie", "Alice", "Bob"};
Arrays.sort(arr, String.CASE_INSENSITIVE_ORDER);

// TreeSet / TreeMap — use natural or provided order
TreeSet<Person> set = new TreeSet<>(byAge);
TreeMap<Person, String> map = new TreeMap<>(byNameThenAge);
  

Sorting with Streams

  List<Person> sorted = people.stream()
    .sorted(Comparator.comparingInt(Person::age))
    .toList();
  

Best Practices

  • Implement Comparable only when a single natural order is meaningful
  • Use Comparator.comparing and method references instead of anonymous classes
  • Chain comparators with thenComparing for multi-field sorting
  • Ensure compareTo is consistent with equals when sorting collections like TreeSet