comparable and comparator

Comparing the two numeric values like two integers or two float values are easy in Java. You just have to use arithmetic operators.

int a = 1;
int b = 5;

if (a > b) {
    System.out.println("a is greater than b");
} else {
    System.out.println("b is greater than a");
}

Output: b is greater than a

Here we have used the greater than (>) operator to compare two numeric values.

Now, let’s try to create a Student class and compare the objects of that class using the same approach.

public class Student {
    private final int rank;
    private final String name;
    
    public Student(int rank, String name) {
        this.rank = rank;
        this.name = name;
    }
    
    public int getRank() {
        return rank;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return rank + ". " + name;
    }
}

Compare two objects of Student class using greater than operator –

Student tom = new Student(1, "Tom");
Student jerry = new Student(5, "Jerry");

if (tom > jerry) {
    System.out.println("Tom is better than Jerry");
} else {
    System.out.println("Jerry is greater than Tom");
}

You will get a compilation error at the highlighted line stating: The operator > is undefined for the argument type(s) Student, Student.

The reason for this error is pretty simple. This operator is not applicable for objects. Additionally, Java doesn’t know how to compare two students – is it by name or rank? JVM won’t make the guess and politely throw the compilation error.

Comparable Interface

Now the question is – how to compare two student objects? The answer is, you have to implement the Comparable interface.

public interface Comparable<T> {
    public int compareTo(T o);
}

As you can see, the interface is pretty simple. You just have to override one simple method compareTo(T o) and return an int value.

But what value to return? The rule is –

Scenario Return Vale
Input object (o) is greater Positive value
Input object (o) is lesser Negative value
Input object (o) is equal Zero

In our example, let’s assume, you want to compare by rank. Lesser the rank, better the student. You have to write that inside the compareTo() method and use this method during comparison instead of greater than operator. So, you can rewrite the code as below –

public class Student implements Comparable<Student> {
    private final int rank;
    private final String name;
    
    public Student(int rank, String name) {
        this.rank = rank;
        this.name = name;
    }
    
    @Override
    public int compareTo(Student o) {
        return rank - o.getRank();
    }
    
    public int getRank() {
        return rank;
    }

    public String getName() {
        return name;
    }
}
Student tom = new Student(1, "Tom");
Student jerry = new Student(5, "Jerry");

if (tom.compareTo(jerry) < 0) {
    System.out.println("Tom is better than Jerry");
} else {
    System.out.println("Jerry is greater than Tom");
}

Output: Tom is better than Jerry

As we have seen, the Comparable interface defines the natural or default ordering of the objects of a class. We can even sort a Student collection using the Collectios.sort() metod.

Student student1 = new Student(1, "Tom");
Student student2 = new Student(5, "Jerry");
Student student3 = new Student(4, "Harry");
Student student4 = new Student(2, "Jack");
Student student5 = new Student(3, "Rose");

List<Student> studentList = Arrays.asList(student1, student2, student3, student4, student5);
Collections.sort(studentList);
System.out.println(studentList);

Output: [1. Tom, 2. Jack, 3. Rose, 4. Harry, 5. Jerry]

When we pass a collection to the Collections.sort() method, objects of that collection are sorted based on their natural order defined by the CompareTo() method.

Limitation with Comparable interface

Using the Comparable interface you can implement only one way of comparison. Let’s assume, you want to sort based on name. You can say – well, change the comparison logic inside compareTo() method. That will in fact work. But what if you want to compare based on both name and rank?

Don’t worry. We can do that as well. Just use the Comparator interface and provide the custom comparison logic as you want.

Comparator interface
public interface Comparator<T> {
   int compare(T o1, T o2);
}

As you can see, the interface is similar to Comparable interface. You just have to override compare() method and return an int value.

But what value to return? The rule is similar to compareTo() method –

Scenario Return Vale
o1 is greater than o2 Positive value
o1 is less than o2 Negative value
o1 and o2 is equal Zero

Let’s first create two implementations of Comparator interface.

public class CompareByName implements Comparator<Student> {
    public int compare(Student obj1, Student obj2) {
        return obj1.getName().compareTo(obj2.getName());
    }
}
public class CompareByRank implements Comparator<Student> {
    public int compare(Student obj1, Student obj2) {
        return obj2.getRank() - obj2.getRank();
    }
}

In collections.sort() method, along with providing the collection, you can also provide a comparator based on which collection will be sorted.

Student student1 = new Student(1, "Tom");
Student student2 = new Student(5, "Jerry");
Student student3 = new Student(4, "Harry");
Student student4 = new Student(2, "Jack");
Student student5 = new Student(3, "Rose");

List<Student> studentList = Arrays.asList(student1, student2, student3, student4, student5);
Collections.sort(studentList);
System.out.println(studentList);

Collections.sort(studentList, new CompareByRank());
System.out.println(studentList);

Collections.sort(studentList, new CompareByName());
System.out.println(studentList);

Output:

[1. Tom, 2. Jack, 3. Rose, 4. Harry, 5. Jerry]
[1. Tom, 2. Jack, 3. Rose, 4. Harry, 5. Jerry]
[4. Harry, 2. Jack, 5. Jerry, 3. Rose, 1. Tom]

Please note, the default sorting order is ascending. You can also sort in descending order using following approach –

Student student1 = new Student(1, "Tom");
Student student2 = new Student(5, "Jerry");
Student student3 = new Student(4, "Harry");
Student student4 = new Student(2, "Jack");
Student student5 = new Student(3, "Rose");

List<Student> studentList = Arrays.asList(student1, student2, student3, student4, student5);
Collections.sort(studentList, Collections.reverseOrder());
System.out.println(studentList);
	
Collections.sort(studentList, new CompareByName().reversed());
System.out.println(studentList);

Output:

[5. Jerry, 4. Harry, 3. Rose, 2. Jack, 1. Tom]
[1. Tom, 3. Rose, 5. Jerry, 2. Jack, 4. Harry]
TreeSet

TreeSet is one of the implementations of the Set interface where elements are ordered based on their natural ordering. You can also provide a Comparator at set creation time for custom sorting.

Student student1 = new Student(1, "Tom");
Student student2 = new Student(5, "Jerry");
Student student3 = new Student(4, "Harry");
Student student4 = new Student(2, "Jack");
Student student5 = new Student(3, "Rose");

List<Student> studentList = Arrays.asList(student1, student2, student3, student4, student5);

Set<Student> studentSet1 = new TreeSet<Student>(studentList);
studentSet1.addAll(studentList);

Set<Student> studentSet2 = new TreeSet<Student>(new CompareByName());
studentSet2.addAll(studentList);

System.out.println(studentSet1);
System.out.println(studentSet2);

Output:

[1. Tom, 2. Jack, 3. Rose, 4. Harry, 5. Jerry]
[4. Harry, 2. Jack, 5. Jerry, 3. Rose, 1. Tom]
TreeMap

TreeMap is one of the implementations of the Map interface where elements are ordered based on their natural ordering of its keys. You can also provide a Comparator at Map creation time for custom sorting.

Map<Integer, String> studentMap1 = new TreeMap<Integer, String>();
studentMap1.put(3, "Rose");
studentMap1.put(1, "Tom");
studentMap1.put(2, "Jack");

Map<Integer, String> studentMap2 = new TreeMap<Integer, String>(Collections.reverseOrder());
studentMap2.put(3, "Rose");
studentMap2.put(1, "Tom");
studentMap2.put(2, "Jack");

System.out.println(studentMap1);
System.out.println(studentMap2);

Output:

{1=Tom, 2=Jack, 3=Rose}
{3=Rose, 2=Jack, 1=Tom}
Difference between Comparable and Comparator

As you have already seen, there are few differences between Comparable and Comparator interfaces. I’ll list down the differences below –

Comparable Comparator
Comparable interface present in java.lang package. Comparator interface present in java.util package.
Comparable provides compareTo() method to sort elements. Comparator provides compare() method to sort elements.
Comparable provides a single way of sorting. Comparator can provide multiple ways of sorting.
Comparable provides natural sorting order. Comparator provides custom sorting order.
Contract between equals, Comparable and Comparator

We know that, if Comparable returns 0 it means two objects are the same by comparison. Also By equals, we check if two objects are the same if it returns true. So the contract is: if two objects are equal by equality check then those objects must return 0 by Comparable or Comparator.

That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.