Hey, Tea Lovers! Today let’s look at the most asked interview question, Comparable vs Comparator. Both beginners and experienced people get this question in their interviews. This post is one of the many in the series of Interview Questions, where we don’t just hang you with one line answer, but rather try to explain it so that it gets fits into your head.
I would be happy to connect with you guys on social media. It’s @coderstea on Twitter, Linkedin, Facebook, Instagram, and YouTube.
Please Subscribe to the newsletter to know about the latest posts from CodersTea.
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 do we use a Comparator or Comparable?
First, we need to understand why comparable & comparator comes into the picture. They give you the answer of comparison. Like, as which one is greater and 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’s understand with a coding example, First, we will sort int, String & List then 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(", ")));
Code language: JavaScript (javascript)
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
Code language: JavaScript (javascript)
As above console output, we can see that with the java inbuild primitive & wrapper class the sorting is working properly, let’s 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
}
Code language: PHP (php)
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() + " "));
}
Code language: PHP (php)
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)
Code language: JavaScript (javascript)
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 a custom class, it starts the search for 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 logic.
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.
Method | Description |
---|---|
public int compareTo(Object object) | Compares this object with the specified object for order & return, a positive integer, if the current object is greater than the specified object. a 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
}
Code language: PHP (php)
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() +" "));
}
Code language: PHP (php)
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
Code language: JavaScript (javascript)
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 passing 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, and the below are required frequently. to get a full list click here.
Method | Description |
---|---|
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 |
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 );
}
}
Code language: PHP (php)
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() +" "));
}
Code language: PHP (php)
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. No | Comparable | Comparator |
---|---|---|
1 | Comparable provides a single sorting sequence. In other words, we can sort the collection based on a single element such as rollno, and name. | The Comparator provides multiple sorting sequences. In other words, we can sort the collection based on multiple elements such as rollno, and name. |
2 | Comparable affects the original class, which means the actual class is modified. | The comparator doesn’t affect the original class, which means the actual class is not modified. |
3 | Comparable provides compareTo() method to sort elements. | Comparator provides compare() method to sort elements. |
4 | Comparable is in java.lang package. | The comparator is in java.util package. |
5 | We can sort the list elements of Comparable type by Collections.sort(List) method. | We can sort the list elements of Comparator type by the Collections.sort(List, Comparator) method. |
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, 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 based on multiple elements such as rollno, and name.
Due to this, the Comparator is very useful. It is used widely in the 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 comparison. For example, the Integer class already provided 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 the 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 Comparators, 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 Questions, where we don’t just hang you with one line answer, but rather try to explain it so that it gets fits into your head.
You can learn more about other Java features such as Stream API, Best Practices, and Functional 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. HAKUNA MATATA!!!
I would be happy to connect with you guys on social media. It’s @coderstea on Twitter, Linkedin, Facebook, Instagram, and YouTube.
Please Subscribe to the newsletter to know about the latest posts from CodersTea.