Comparable & Comparator
Comparable — Natural Ordering
Implementing Comparable for natural ordering
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Employee implements Comparable<Employee> {
String name;
int age;
Employee(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Employee other) {
return Integer.compare(this.age, other.age); // natural order: by age
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
List<Employee> team = new ArrayList<>(List.of(
new Employee("Diego", 34),
new Employee("Amara", 28),
new Employee("Zoe", 41)
));
Collections.sort(team);
System.out.println(team);[Amara(28), Diego(34), Zoe(41)]
Comparator — External, Alternative Orderings
A Comparator sorting the same class by name instead
import java.util.Comparator;
import java.util.List;
List<Employee> team = new ArrayList<>(List.of(
new Employee("Diego", 34),
new Employee("Amara", 28),
new Employee("Zoe", 41)
));
Comparator<Employee> byName = new Comparator<Employee>() {
@Override
public int compare(Employee a, Employee b) {
return a.name.compareTo(b.name);
}
};
team.sort(byName);
System.out.println(team);[Amara(28), Diego(34), Zoe(41)]
Modern Comparators with Lambdas
Comparator.comparing() and lambdas
import java.util.Comparator;
import java.util.List;
List<Employee> team = new ArrayList<>(List.of(
new Employee("Diego", 34),
new Employee("Amara", 28),
new Employee("Zoe", 41)
));
// Equivalent to the anonymous class above, but far shorter
team.sort(Comparator.comparing(e -> e.name));
System.out.println(team);
// Sort by age descending
team.sort(Comparator.comparing((Employee e) -> e.age).reversed());
System.out.println(team);[Amara(28), Diego(34), Zoe(41)] [Zoe(41), Diego(34), Amara(28)]
Chaining with thenComparing()
Chaining comparators: age first, then name as a tiebreaker
import java.util.Comparator;
import java.util.List;
List<Employee> team = new ArrayList<>(List.of(
new Employee("Zoe", 34),
new Employee("Amara", 34),
new Employee("Diego", 28)
));
team.sort(
Comparator.comparing((Employee e) -> e.age)
.thenComparing(e -> e.name)
);
System.out.println(team);[Diego(28), Amara(34), Zoe(34)]
Aspect | Comparable<T> | Comparator<T> |
|---|---|---|
Defined | Inside the class being sorted | As a separate, external object |
Method | compareTo(T other) | compare(T a, T b) |
Number of orderings | One — the "natural" order | As many as you like |
Use when | You own the class and there's one obvious order | You need alternate orders, or can't modify the class |
Modern helpers | n/a | Comparator.comparing(), .reversed(), .thenComparing() |
Comparable defines one natural ordering inside the class via compareTo()
Comparator defines an external ordering via compare(), and you can have several
Comparator.comparing() builds a Comparator from a key-extractor lambda
.reversed() flips an ordering; .thenComparing() adds a tiebreaker