Comparable and Comparator Interfaces in Java

Comparable and Comparator Interfaces in Java | Both Comparable and Comparator interfaces are used in the sorting of elements. Let us see them in detail.

Comparable Interface In Java

It is present in java.lang package and it contains only one method compareTo(). Method Signature:- public int compareTo(T o);

In most of the pre-defined classes including String, StringBuffer, StringBuilder, and all Wrapper classes compareTo() is implemented to return the following:- obj1.compareTo(obj2)

  1. It returns -ve if obj1 has to come before obj2. (obj1 < obj2)
  2. It returns +ve if obj1 has to come after obj2. (obj1 > obj2
  3. It returns 0 if obj1 & obj2 are equal. (obj1 == obj2)

In obj1.compareTo(obj2), here:-
1. obj1:- The object which is to be inserted.
2. Obj2:- The object which is already inserted.

public class Test {
   public static void main(String[] args) {
      System.out.println("A".compareTo("Z")); // -25
      System.out.println("Z".compareTo("K")); // 15
      System.out.println("A".compareTo("A")); // 0
      System.out.println("A".compareTo(null)); // NullPointerException
   }
}

If we are depending on the default natural sorting order then while adding objects into the TreeSet JVM will call the compareTo() method.

import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet<>();
        ts.add("K"); // First element inserted without comparision
        ts.add("Z"); // "Z".compareTo("K") => +ve
        System.out.println(ts); // [K, Z]

        ts.add("A"); // "A".compareTo("K") => -ve
        System.out.println(ts); // [A, K, Z]

        ts.add("A");
        /*
         * "A".compareTo("K") => -ve; before adding this statement we already got -ve,
         * So compare with all other values where we got -ve. "A".compareTo("A") => 0;
         * Duplicate
         */
        System.out.println(ts); // [A, K, Z]

    }
}
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        TreeSet<A> treeSet = new TreeSet<>();
        treeSet.add(new A("Java")); // java.lang.ClassCastException
        treeSet.add(new A("Know"));
        treeSet.add(new A("Program"));
        System.out.println(treeSet);
    }
}

public class A {
    String a;

    A(String a) {
        this.a = a;
    }
}

It gives:- Exception in thread “main” java.lang.ClassCastException: class A cannot be cast to class java.lang.Comparable (A is in the unnamed module of loader ‘app’; java.lang.Comparable is in module java.base of loader ‘bootstrap’)

If the default natural sorting order is not available or if we are not satisfied with the default natural sorting order then we can go for a customized sorting order by using Comparator(I).

Comparator Interface In Java

Difference between the Comparable and Comparator interface? Comparable is meant for default natural sorting order whereas Comparator is meant for Customized sorting order.

Comparator interface present in java.util package and it declare 2 methods compare() and equals().

  • public int compare(Object obj1, Object obj2)
    • It returns -ve if obj1 has to come before obj2.
    • It returns +ve if obj1 has to come after obj2.
    • It returns 0 if obj1 & obj2 are equal.
  • public boolean equals(Object obj)

Whenever we are implementing the Comparator interface then we must provide implementation at least for the compare() method. It is not compulsory to provide the implementation for the equals() method i.e. implementing the equals() method is optional because it is already available in our class from java.lang.Object class through inheritance.

class MyComparator implements Comparator { 
   public int compare(Object obj1, Object obj2) { 
      // ....
      return 0; 
   } 
}

Example Using Comparator Interface

1. Write a program to insert an integer object into the TreeSet where sorting order is in descending order.

import java.util.Comparator;

public class MyComparator implements Comparator<Integer> {

    @Override
    public int compare(Integer i1, Integer i2) {
        if (i1 < i2) {
            return +1;
        } else if (i1 > i2) {
            return -1;
        } else {
            return 0;
        }
    }

}
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<Integer> treeSet = new TreeSet<>(new MyComparator());
        treeSet.add(10);
        treeSet.add(0);
        treeSet.add(15);
        treeSet.add(5);
        treeSet.add(20);
        treeSet.add(20);
        System.out.println(treeSet); // [20, 15, 10, 5, 0]
    }
}

In this program:-

treeset.add(10); // For 1st element Comparision is not required
treeset.add(0); // compare(0, 10) => +ve
treeset.add(15); // compare(15, 10) => -ve
treeset.add(5); // compare(5, 10) => +ve; compare(5, 0) => -ve
treeset.add(20); // compare(20, 10) => -ve; compare(20, 15) => -ve
treeset.add(20); // compare(20, 10) => -ve; compare(20, 15) => -ve; compare(20, 20) => 0

If we are using the constructor TreeSet() i.e. not passing the Comparator(I) object to the constructor then internally JVM will call the compareTo() method which is meant for default natural sorting order.

import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<Integer> treeSet = new TreeSet<>();
        treeSet.add(10);
        treeSet.add(0);
        treeSet.add(15);
        treeSet.add(5);
        treeSet.add(20);
        treeSet.add(20);
        System.out.println(treeSet); // [0, 5, 10, 15, 20]
    }
}

If we are using the constructor TreeSet(Comparator c) i.e. passing the Comparator(I) object to the constructor then internally JVM will call the compare() method which is meant for customized sorting order:- Set<Integer> treeSet = new TreeSet<>();

In the MyComparator class, we can directly call the Integer class compareTo() method which does the same as we are doing in the compare() method:-

import java.util.Comparator;

public class MyComparator implements Comparator<Integer> {

    @Override
    public int compare(Integer i1, Integer i2) {
        return i1.compareTo(i2);
    }

}

Creating a separator class provides reusability, but if we are not getting to use it again then there is no point in creating a separate MyComparator class. Instead, we can provide code while creating the TreeSet object.

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<Integer> treeSet = new TreeSet<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return - o1.compareTo(o2);
            }
        });
        treeSet.add(10);
        treeSet.add(0);
        treeSet.add(15);
        treeSet.add(5);
        treeSet.add(20);
        treeSet.add(20);
        System.out.println(treeSet); // [20, 15, 10, 5, 0]
    }
}

Various possible implementation in the compare() method:-

  • return i1.compareTo(i2); => Default natural sorting order, [0, 5, 10, 15, 20]
  • return – i1.compareTo(i2); => Descending Order, [20, 15, 10, 5, 0]
  • return i2.compareTo(i1); => Descending Order, [20, 15, 10, 5, 0]
  • return - i2.compareTo(i1); => Default natural sorting order, [0, 5, 10, 15, 20]
  • return +1; => Insertion Order, [10, 0, 15, 5, 20, 20]
  • return -1; => Reverse of Insertion Order, [20, 20, 5, 15, 0, 10]
  • return 0; => [10], Only 1st element will be inserted & all remaining are considered as duplicate.

The Comparator class has also provided some static methods like reverseOrder(), naturalOrder(), nullsFirst(), nullsLast() which can be used while sorting.

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<Integer> treeSet = new TreeSet<>(Comparator.reverseOrder());
        treeSet.add(10);
        treeSet.add(0);
        treeSet.add(15);
        treeSet.add(5);
        treeSet.add(20);
        treeSet.add(20);
        System.out.println(treeSet); // [20, 15, 10, 5, 0]
    }
}

2. Write a program to insert the String object into the TreeSet where all elements should be inserted according to the reverse of alphabetical order.

Before writing a program to sort according to the reverse of alphabetical order, first, let us see the default natural sorting order. The String class overrides the compareTo() method to sort elements in the ascending order of the alphabet.

import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("Pineapple");
        treeSet.add("Apple");
        treeSet.add("Orange");
        treeSet.add("Grapes");
        treeSet.add("Banana");
        System.out.println(treeSet); 
        // [Apple, Banana, Grapes, Orange, Pineapple]
    }
}

To sort in reverse alphabetical order we have to use the Comparator interface as follows:-

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<String> treeSet = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return -o1.compareTo(o2);
            }
        });
        treeSet.add("Pineapple");
        treeSet.add("Apple");
        treeSet.add("Orange");
        treeSet.add("Grapes");
        treeSet.add("Banana");
        System.out.println(treeSet);
        // [Pineapple, Orange, Grapes, Banana, Apple]
    }
}

The same program can be written using Comparator.reverseOrder() method as follows:-

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<String> treeSet = new TreeSet<>(Comparator.reverseOrder());
        treeSet.add("Pineapple");
        treeSet.add("Apple");
        treeSet.add("Orange");
        treeSet.add("Grapes");
        treeSet.add("Banana");
        System.out.println(treeSet);
        // [Pineapple, Orange, Grapes, Banana, Apple]
    }
}

Write a program to insert a StringBuffer object into the TreeSet where the sorting order is alphabetical. This program is similar to String because StringBuffer also implements Comparable(I).

3. Write a program to insert String objects in TreeSet where the sorting order is increasing in length order. If two objects have the same length then consider their alphabetical order.

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<String> treeSet = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                if (o1.length() > o2.length()) {
                    return 1;
                } else if (o1.length() < o2.length()) {
                    return -1;
                } else {
                    // natural sorting order
                    return o1.compareTo(o2);
                }
            }
        });
        treeSet.add("Apple");
        treeSet.add("Banana");
        treeSet.add("Orange");
        treeSet.add("Grapes");
        treeSet.add("Pineapple");
        treeSet.add("Pear");
        System.out.println(treeSet);
        // [Pear, Apple, Banana, Grapes, Orange, Pineapple]
    }
}

The same program can be written as follows:-

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        Set<String> treeSet = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                Integer val = Integer.compare(s1.length(), s2.length());
                if (val != 0) {
                    return val;
                } else {
                    return s1.compareTo(s2); // natural sorting order
                }
            }
        });
        treeSet.add("Apple");
        treeSet.add("Banana");
        treeSet.add("Orange");
        treeSet.add("Grapes");
        treeSet.add("Pineapple");
        treeSet.add("Pear");
        System.out.println(treeSet);
        // [Pear, Apple, Banana, Grapes, Orange, Pineapple]
    }
}

The compare() method of the Integer wrapper class works as:- Integer.valueOf(x).compareTo(Integer.valueOf(y))

Comparable vs Comparator Interfaces

When we should use Comparable vs Comparator?

  • For pre-defined classes default natural sorting order is already available. If we are not satisfied with that default natural sorting order then we can define our sorting order by using the Comparator interface.
  • For pre-defined non-comparable classes default natural sorting isn’t available, for them, we can define our sorting by using Comparator.
  • For our classes like Employee, Product, and Student classes, the programmer who is developing those classes is responsible for defining default natural sorting by implementing the Comparable interface. The person who is using those classes, if he is not satisfied with default natural sorting then he can define his sorting by using Comparator.

For example:- we have an Employee class and by default, it should sort employees in ascending order based on the employee ID value. Later if we want to sort based on the name in alphabetic order then we can provide our implementation.

public class Employee implements Comparable<Employee> {

    private Integer id;
    private String name;
    private Double salary;

    public Employee(Integer id, String name, Double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

     // getter and setter methods

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", salary=" + salary + "]";
    }

    @Override
    public int compareTo(Employee emp) {
        return this.getId().compareTo(emp.getId());
    }
}
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        TreeSet<Employee> treeSet = new TreeSet<>();
        treeSet.add(new Employee(101, "John", 50000.0));
        treeSet.add(new Employee(105, "David", 52000.0));
        treeSet.add(new Employee(102, "Alice", 60000.0));
        treeSet.add(new Employee(104, "Jane", 58000.0));
        treeSet.add(new Employee(103, "Bob", 55000.0));
        
        System.out.println(treeSet);
        // [Employee [id=101, name=John, salary=50000.0],
        // Employee [id=102, name=Alice, salary=60000.0],
        // Employee [id=103, name=Bob, salary=55000.0],
        // Employee [id=104, name=Jane, salary=58000.0],
        // Employee [id=105, name=David, salary=52000.0]]
    }
}

Sort employees based on ascending order of the name:-

TreeSet<Employee> treeSet = new TreeSet<>(new Comparator<Employee>() {

    @Override
    public int compare(Employee o1, Employee o2) {
        return o1.getName().compareTo(o2.getName());
    }
});

Sort employees based on descending order of salary:-

TreeSet<Employee> treeSet = new TreeSet<>(new Comparator<Employee>() {

    @Override
    public int compare(Employee o1, Employee o2) {
        // return o2.getSalary().compareTo(o1.getSalary());
        return Double.compare(o2.getSalary(), o1.getSalary());
    }
});

Sort employees based on the descending order of their salary, if the salary is the same then sort them based on the ascending order of the name, if they have the same name then sort them based on the employee id.

import java.util.Comparator;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {
        TreeSet<Employee> treeSet = new TreeSet<>(new Comparator<Employee>() {

            @Override
            public int compare(Employee o1, Employee o2) {
                // First, compare by salary in descending order
                Integer salaryComparison = Double.compare(o2.getSalary(), o1.getSalary());
                if (salaryComparison != 0) {
                    return salaryComparison;
                } else {
                    // If salary is the same, compare by name in ascending order
                    Integer nameComparison = o1.getName().compareTo(o2.getName());
                    if (nameComparison != 0) {
                        return nameComparison;
                    } else {
                        // If name is also the same, compare by ID in ascending order
                        return o1.getId().compareTo(o2.getId());
                    }
                }
            }
        });
        treeSet.add(new Employee(101, "John", 60000.0));
        treeSet.add(new Employee(102, "Alice", 55000.0));
        treeSet.add(new Employee(103, "Bob", 60000.0));
        treeSet.add(new Employee(104, "Alice", 55000.0));
        treeSet.add(new Employee(105, "David", 60000.0));
        System.out.println(treeSet);
    }
}
ComparableComparator
It is meant for default natural sorting order.It is meant for customized sorting orders.
It is present in java.lang package.It is present in java.util package.
It defines only 1 abstract method:- compareTo()It defines 2 abstract methods:- compare(), equals()
String, StringBuffer, StringBuilder, and all wrapper classes implement Comparable(I)Since it is meant for customized sorting therefore generally pre-defined classes won’t implement Comparator(I). The only implemented pre-defined classes of Comparator(I) are Collator, and RuleBasedCollator which are used in GUI-based applications.

If you enjoyed this post, share it with your friends. Do you want to share more information about the topic discussed above or do you find anything incorrect? Let us know in the comments. Thank you!

Leave a Comment

Your email address will not be published. Required fields are marked *