Java Comparable and Comparator
Comparable and Comparator tell Java how to sort objects. Use Comparable for a class’s natural order, and use Comparator when you want a separate or alternate order.
Strings and numbers already know how to compare themselves. Your own classes, such as Student or Product, need comparison rules before Java can sort them in a meaningful way.
Comparable: Natural Order
A class implements Comparable<T> when it has one main sorting order. The class must define the compareTo() method.
import java.util.ArrayList;
import java.util.Collections;
public class Main {
static class Student implements Comparable<Student> {
String name;
int grade;
Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
public int compareTo(Student other) {
return Integer.compare(this.grade, other.grade);
}
public String toString() {
return name + "(" + grade + ")";
}
}
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<>();
students.add(new Student("Mia", 92));
students.add(new Student("Ava", 85));
students.add(new Student("Noah", 90));
Collections.sort(students);
System.out.println(students);
}
}
Output:
[Ava(85), Noah(90), Mia(92)]
The compareTo() method returns a negative number when the current object should come before the other object, zero when they are equal for sorting, and a positive number when it should come after. Here, Integer.compare(this.grade, other.grade) sorts students from lowest grade to highest grade.
Comparator: Custom Order
A Comparator<T> is a separate object or lambda expression that compares two values. It is useful when you cannot edit the class, or when you need more than one sorting order.
import java.util.ArrayList;
import java.util.Comparator;
public class Main {
static class Student {
String name;
int grade;
Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
public String toString() {
return name + "(" + grade + ")";
}
}
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<>();
students.add(new Student("Mia", 92));
students.add(new Student("Ava", 85));
students.add(new Student("Noah", 90));
students.sort(Comparator.comparing(student -> student.name));
System.out.println("By name: " + students);
students.sort(Comparator.comparingInt((Student student) -> student.grade).reversed());
System.out.println("By grade: " + students);
}
}
Output:
By name: [Ava(85), Mia(92), Noah(90)]
By grade: [Mia(92), Noah(90), Ava(85)]
The first sort uses Comparator.comparing() to sort by name. The second sort uses Comparator.comparingInt() and reversed() to sort by grade from highest to lowest.
Comparable Versus Comparator
| Feature | Comparable |
Comparator |
|---|---|---|
| Where rule lives | Inside the class being sorted | Outside the class being sorted |
| Main method | compareTo(other) |
compare(a, b) or helper methods |
| Best use | One natural order | Many possible orders |
| Common examples | Student by ID, task by due date | Sort by name, then price, then rating |
Sorting Collections
For lists, you can use Collections.sort(list) when the elements implement Comparable. You can also use list.sort(comparator) or Collections.sort(list, comparator) when you have a comparator.
Sorted collections such as TreeSet and TreeMap also need comparison rules. Their elements or keys must either have a natural order or be given a comparator when the collection is created.
Common Mistakes
- Returning only
1or-1and never returning0for equal values. - Subtracting numbers inside comparisons, such as
a - b, whenInteger.compare(a, b)is safer and clearer. - Expecting
Collections.sort()to work on custom objects withoutComparableor aComparator. - Forgetting that changing the comparator changes the meaning of sorted order in collections such as
TreeSet.
Takeaway: use Comparable for a class’s normal sort order, and use Comparator whenever you need a custom sorting rule.
