How do we compare apples with oranges? Or even apples with apples, for that matter? Do we compare based on size or colour or shape or sweetness?
We often need to compare objects in Java. Sorting, for example, is based on comparison according to some criteria. So it is important to understand the difference between Comparable
and Comparator
.
The Comparable interface in Java
If a class has a natural sorting order, we should implement the Comparable
interface. This ensures that objects of this class can automatically be sorted when we add them to an ordered collection, such as a TreeSet
or a TreeMap
.
public interface Comparable { public int compareTo(T other); }
The compareTo()
method should return the following:
- A negative number if the current object (
this
) is “less” than theother
object. - A positive number if the current object (
this
) is “greater” than theother
object. - Zero if the two objects are the same.
The meanings of “less”, “greater” and “equal” are entirely dependent on our application.
Note that equals()
should be consistent with compareTo()
, i.e. if equals()
returns true
, then compareTo()
must return 0
(zero).
This sorting is referred to as the natural ordering of the class. Natural ordering is obvious for numbers and strings.
But what should we do when there isn’t an obvious (or natural) way to sort the objects? For example, how would we “naturally” sort employees? By weight, height, age, name, qualifications, salary, experience, etc.?
The Comparator interface in Java
If there isn’t an obvious way to sort our domain class, then we should not implement the Comparable
interface.
Instead, we should create an object that implements the Comparator
interface, and pass that object to the required ordered collection. The collection then uses that Comparator
object to sort the objects in the specific way we have defined.
public interface Comparator { int compare(T o1, T o2); }
The compare()
method should have the same semantic behaviour as the compareTo()
method.
Example code
Let’s compare (pun intended) code implementing the Comparable
and the Comparator
interfaces. As an example, let’s say that we want to compare Employee
objects based on age.
We can implement Comparable
in the Employee
class:
public class Employee implements Comparable { // other fields private int age; // other methods @Override public int compareTo(Employee other) { return this.age - other.age; } }
Adding Comparable
employees to an ordered collection would be as simple as:
TreeSet ts = new TreeSet<>(); // adding lots of employee objects, sorting automatically Employee e1 = new Employee(); ts.add(e1);
If there was no natural ordering, or we wanted to change the sorting algorithm on the fly, we would create a Comparator
, and pass it to the ordered collection:
Comparator cmp = new Comparator<>() { @Override public int compare(Employee e1, Employee e2) { return e1.age - e2.age; } }; TreeSet ts = new TreeSet<>(cmp); // adding lots of employee objects, sorting automatically Employee e1 = new Employee(); ts.add(e1);
Conclusion
Which interface should you choose? Very easy! If there’s only one obvious, natural, unchanging way you want to sort objects, implement Comparable
. If you want to change the sorting algorithm depending on what you need at the time, implement Comparator
.
I’m always interested in your opinion, so please leave a comment. Your feedback helps me write tips that help you.