Interview Question: Comparable vs Comparator

Hey, Tea Lovers! Today let’s look at the most asked interview question, Comparable vs Comparator. Both beginners and experienced persons get this question in their interviews. This post is one of the many in the series of Interview Question, where we don’t just hang you with one line answer, rather try to explain it so that it gets fit into your head.

I know you are in a hurry to prepare for the interview, so I will make this post as short as possible, So prepare your cup of tea to sip and code.

Why We use Comparator or Comparable

First, we need to understand why comparable & comparator comes into the picture. They give you the answer of comparison. Like, which one is greater which one is smaller. And the main reason for doing that is to Sort the collection of objects. We can easily compare the Integer or String naturally, but the complex or regular POJO object has multiple fields. In that case, we need to specify how we are determining which objects should come first or which one is greater.

In java String and Wrapper classes had implemented the Comparable interface. So if you store string or wrapper class objects then it will be used Comparable & get Sorted.

Sorting

Sorting is any process of arranging items systematically. Comparable and Comparator interfaces use Generics for compile-time type checking.
Collections class provides static methods for sorting the elements of a collection. If collection elements are of a Set type, we can use TreeSet. However, we cannot sort the elements of the List. Collections class provides methods for sorting the elements of List type elements.

Example of Sorting

Let understand with a coding example, First, we will sort int, String & List than after we create one custom Student class and try to understand what was the issue. The full source code can be found on GitHub.

Code

//Primitive Data-type Integer Array sort
int[] intArray = {8, 1, 10, 2};
out.println("Primitive Data-type Integer Array Before sort : " + Arrays.toString(intArray));
Arrays.sort(intArray);
out.println("Primitive Data-type Integer Array After sort : " + Arrays.toString(intArray) + "\n");

//In-build Class String Array sort
String[] strArray = {"C", "O", "D", "E", "R", "S", "T", "E", "A"};
out.println("In-build Class String Array Before sort : " + Arrays.toString(strArray));
Arrays.sort(strArray);
out.println("In-build Class String Array After sort : " + Arrays.toString(strArray) + "\n");

//List Collection of String type sort
List<String> listOfStringType = new ArrayList<>(Arrays.asList("C", "O", "D", "E", "R", "S", "T", "E", "A"));

out.println("List Collection of String type Before sort : " + listOfStringType.stream().collect(Collectors.joining(", ")));
Collections.sort(listOfStringType); //Sorting From Collection Class as Mentioned Above
out.println("List Collection of String type After sort : " + listOfStringType.stream().collect(Collectors.joining(", ")));

Output

Primitive Data-type Integer Array Before sort : [8, 1, 10, 2]
Primitive Data-type Integer Array After sort : [1, 2, 8, 10]

In-build Class String Array Before sort : [C, O, D, E, R, S, T, E, A]
In-build Class String Array After sort : [A, C, D, E, E, O, R, S, T]

List Collection of String type Before sort : C, O, D, E, R, S, T, E, A
List Collection of String type After sort : A, C, D, E, E, O, R, S, T

As above console output, we can see that with java inbuild primitive & wrapper class the sorting working properly, let see the same with a custom class.

Custom class Code example.

Code

class Student {
    private int rollNo;
    private String name;
    private int classNo;
    private String divison;

    // constructor, getters, and setters
}
public static void compareAndSortStudentsWithoutComparisionLogic() {
    Student[] studentArray = new Student[4];
    studentArray[0] = new Student(3, "Veronika", 10, "C");
    studentArray[1] = new Student(1, "Sym", 8, "D");
    studentArray[2] = new Student(31, "Mahesh", 11, "B");
    studentArray[3] = new Student(2, "Imran", 9, "A");
    
    out.print("Custom Student Class Array Before sort : ");
    Stream.of(studentArray).forEach(e -> out.print(e.getRollNo() + " "));
    
    Arrays.sort(studentArray);
    
    out.print("\nCustom Student Class Array After sort : ");
    Stream.of(studentArray).forEach(e -> out.print(e.getRollNo() + " "));
}

Output

Custom Student Class Array Before sort : 3 1 31 2 Exception in thread "main" java.lang.ClassCastException: class sd.sym.initiative.interview.collection.Student cannot be cast to class java.lang.Comparable (sd.sym.initiative.interview.collection.Student is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
	at java.base/java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
	at java.base/java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
	at java.base/java.util.Arrays.sort(Arrays.java:1249)
	at sd.sym.initiative.interview.collection.SortStudentClass.main(SortStudentClass.java:75)

Here is a problem, When you try to run this, it throws the runtime exception as the Student class cannot be cast to class java.lang.Comparable. Because when JVM tried to sort custom class, it starts the search the logic for comparing this custom class. Since you didn’t define any, it throws the Exception.

Now let us look at the ways to implement this logics.

Comparable

A Comparable interface in java.lang package is used to order the objects of the user-defined class. It contains only one method named compareTo(Object object)

A Comparable object is capable of comparing itself with another object.

It provides a single comparison only, meaning all comparisons are tied to one specific logic only. You can’t scale with multiple sorting methods depending on the requirement.

MethodDescription
public int compareTo(Object object)Compares this object with the specified object for order & return,

positive integer, if the current object is greater than the specified object.

negative integer, if the current object is less than the specified object.

zero, if the current object is equal to the specified object.

Let’s simply use the previous example with this logic and avoid that annoying error.

Code

The full source code can be found on GitHub.

class Student2 implements Comparable<Student2> {

    private int rollNo;
    private String name;
    private int classNo;
    private String divison;

    @Override
    // the comparisons between this and the another Student2 Object
    public int compareTo(Student2 anotherStudent2) {
        return (Integer.compare(this.rollNo, anotherStudent2.getRollNo()));
    }
   // getters. setters,constructor
}
public static void sortWithComparableOnStudent2() {

    Student2[] student2Array = new Student2[4];
    student2Array[0] = new Student2(3, "Veronika", 10, "C");
    student2Array[1] = new Student2(1, "Sym", 8, "D");
    student2Array[2] = new Student2(31, "Mahesh", 11, "B");
    student2Array[3] = new Student2(2, "Imran", 9, "A");

    out.print("Custom Student Class Array Before sort : ");
    Stream.of(student2Array).forEach(e -> out.print(e.getRollNo() +" "));

    Arrays.sort(student2Array);

    out.print("\nCustom Student Class Array After sort : ");
    Stream.of(student2Array).forEach(e -> out.print(e.getRollNo() +" "));


    //Using Collections Class
    List<Student2> studentList = new ArrayList<>();
    studentList.add(new Student2(3, "Veronika", 10, "C"));
    studentList.add(new Student2(31, "Mahesh", 11, "B"));
    studentList.add(new Student2(2, "Imran", 10, "A"));
    studentList.add(new Student2(1, "Sym", 8, "D"));
    studentList.add(new Student2());
    studentList.add(new Student2());

    out.print("\n\nCustom Student Class list Before sort : ");
    studentList.forEach(e -> out.print(e.getRollNo() +" "));

    //Sort List as per mentioned logic in comparator
    Collections.sort(studentList);
    out.print("\nCustom Student Class list After sort : ");
    studentList.forEach(e -> out.print(e.getRollNo() +" "));

    //Sort List in reverse as per mentioned logic in comparator
    Collections.sort(studentList, Comparator.reverseOrder());
    out.print("\nCustom Student Class list After reverse sort : ");
    studentList.forEach(e -> out.print(e.getRollNo() +" "));
}

Output

Custom Student Class Array Before sort : 3 1 31 2 
Custom Student Class Array After sort : 1 2 3 31 

Custom Student Class list Before sort : 3 31 2 1 0 0 
Custom Student Class list After sort : 0 0 1 2 3 31 
Custom Student Class list After reverse sort : 31 3 2 1 0 0 

Comparator

A Comparator interface is used to order the objects of a user-defined class.

This interface is under java.util package and contains 2 methods compare(Object obj1,Object obj2) and equals(Object element).

The biggest advantage of the Comparator is that it is pluggable. With this, unlike Comparable, you have the flexibility to add as much comparison logic as you want. Simply create an object by implementing a Comparator and pass it to the sorting method. And since Java 8’s lambda showed up, a one-liner is all you need.

The Comparator interface has many methods, below are required frequently. to get a full list click here.

MethodDescription
public int compare(Object obj1, Object obj2)Compares its two arguments for order.
public boolean equals(Object obj)Indicates whether some other object is “equal to” this comparator
Comparator methods

Let’s simply use the previous example with this logic and avoid that annoying error.

Code

class StudentRollNoComparator implements Comparator<Student> {

	@Override
	public int compare(Student student0, Student student1) {
		int studentRollno0 = student0.getRollNo();
		int studentRollno1 = student1.getRollNo();
		return (studentRollno0 == studentRollno1 ? 0 : studentRollno0 < studentRollno1 ? -1 : 1 );
	}
}
public static void compareAndSortOnComparator() {

    //Using Collections Class
    List<Student> studentList = new ArrayList<>();
    studentList.add(new Student(3, "Veronika", 10, "C"));
    studentList.add(new Student(31, "Mahesh", 11, "B"));
    studentList.add(new Student(2, "Imran", 10, "A"));
    studentList.add(new Student(1, "Sym", 8, "D"));

    out.print("\nCustom Student Class list Before sort : ");
    studentList.forEach(out::print);

    //--------- Sorting base on RollNo From StudentRollNoComparable class mentioned logic in comparator

    StudentRollNoComparator studentRollNoComparator = new StudentRollNoComparator();

    Collections.sort(studentList, studentRollNoComparator);
    out.print("\n\nCustom Student Class list based on rollNo After sort : ");
    studentList.forEach(e -> out.print(e.getRollNo() +" "));

    //In Reverse Order
    Collections.sort(studentList, studentRollNoComparator.reversed());
    out.print("\nCustom Student Class list based on rollNo After reversed sort  : ");
    studentList.forEach(e -> out.print(e.getRollNo() +" "));

    //--------- Sorting base on Name From StudentRollNoComparable class mentioned logic in comparator

    Comparator<Student> studentNameComparator = new Comparator<Student>() {
        @Override
        public int compare(Student student0, Student student1) {
            return student0.getName().compareTo(student1.getName());
        }
    }; // with lambda ====>  (student0, student1) -> student0.getName().compareTo(student1.getName());

    Collections.sort(studentList, studentNameComparator);
    out.print("\n\nCustom Student Class list based on name After sort : ");
    studentList.forEach(e -> out.print(e.getName() +" "));

    // you can also pass the at runtime without creating the implementation.
    Collections.sort(studentList, (s1, s2) -> s1.getDivison().compareTo(s2.getDivison()));
    out.print("\n\nyou can also pass the at runtime without creating the implementation. ");
    studentList.forEach(e -> out.print(e.getName() +" "));
    
    // or use the comparators static method
    Collections.sort(studentList, Comparator.comparing(Student::getDivison));
    out.print("\n\nusing comparator's static method ");
    studentList.forEach(e -> out.print(e.getName() +" "));

    Collections.sort(studentList, studentNameComparator.reversed());
    out.print("\nCustom Student Class list based on name After reversed sort : ");
    studentList.forEach(e -> out.print(e.getName() +" "));

}

Output

Custom Student Class list Before sort : Student [rollNo=3, name=Veronika, classNo=10, divison=C]Student [rollNo=31, name=Mahesh, classNo=11, divison=B]Student [rollNo=2, name=Imran, classNo=10, divison=A]Student [rollNo=1, name=Sym, classNo=8, divison=D]

Custom Student Class list based on rollNo After sort : 1 2 3 31 
Custom Student Class list based on rollNo After reversed sort  : 31 3 2 1 

Custom Student Class list based on name After sort : Imran Mahesh Sym Veronika 

you can also pass the at runtime without creating the implementation. Imran Mahesh Veronika Sym 

using comparator's static method Imran Mahesh Veronika Sym 
Custom Student Class list based on name After reversed sort : Veronika Sym Mahesh Imran 

Difference between Comparable and Comparator

Sr. NoComparableComparator
1Comparable provides a single sorting sequence. In other words, we can sort the collection on the basis of a single element such as rollno, name.The Comparator provides multiple sorting sequences. In other words, we can sort the collection on the basis of multiple elements such as rollno, name.
2Comparable affects the original class, which means the actual class is modified.Comparator doesn’t affect the original class, which means the actual class is not modified.
3Comparable provides compareTo() method to sort elements.Comparator provides compare() method to sort elements.
4Comparable is in java.lang package.Comparator is in the java.util package.
5We can sort the list elements of Comparable type by Collections.sort(List) method.We can sort the list elements of Comparator type by Collections.sort(List, Comparator) method.
Difference between Comparable and Comparator

When to Use What, and Why

Let us look at When to use what, where and how…

When to Use Comparable

The custom class defined sorting logic, don’t want to modify from end-user & force end-user to use that logic for sorting.

For example, String and Integer and other Wrapper classes, they by-default implement comparable to us the natural comparison of the types. So we don’t need to write logic.

When to Use Comparator

The Comparator provides multiple sorting sequences. In other words, we can sort the collection on the basis of multiple elements such as rollno, name.

Due to this, the Comparator is very useful. It is used widely as it below example you can see that all application is added only due to this feature.

Don’t want to modify source class or
When you cannot have access to the class which you have to mention sorting

In many cases, we are not able to modify the source code to make our own comparison. For example, the Integer class already provided a logic but if you want to provide some custom logic for sorting, we can do so with Comparator.

Want to define two or more sorting logic

The best-case scenario for Comparator is when we have different comparison logic for various fields and want to sort something based on requirements.

Conclusion

That’s it for this post. We looked at what is Comparable and Comparator, the differences between them, and many more tips on when and what to use.

This post is one of the many in the series of Interview Question, where we don’t just hang you with one line answer, rather try to explain it so that it gets fit into your head.

You can learn more about other Java features such as Stream APIBest PracticesFunctional Programming.

The full source code can be found on GitHub and the Full project is available here.

Please feel free to suggest any changes, such as additional info, or if you have any questions on some points let us know in the comments. We will be happy to help you.

See you in the next post.