On this page
Comparable & Comparator
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
Comparableonly when a single natural order is meaningful - Use
Comparator.comparingand method references instead of anonymous classes - Chain comparators with
thenComparingfor multi-field sorting - Ensure
compareTois consistent withequalswhen sorting collections likeTreeSet