Monday, February 28, 2022

Comparable and Comparator in Java

Java provider Comparable and Comparator interface for sorting array/collection of objects. By using these interface we can sort primitive and wrapper classes of array or list. 

Here we will see the example of default sort order for primitive types by using java.util.Arrays.sort(). This will sort the primitive by natural order. 

import java.util.Arrays;

public class ComparePrimitive {
public static void main(String[] args) {
int[] intArray = {6, 4, 3, 1, 2, 5};
String[] strArray = {"Java", "Css", "Angular", "Python", "Xml"};
Arrays.sort(intArray);
Arrays.sort(strArray);
System.out.println("Sorted Array of int: " + Arrays.toString(intArray));
System.out.println("Sorted Array of String: " + Arrays.toString(strArray));
}
}

Output: 
Sorted Array of int: [1, 2, 3, 4, 5, 6]
Sorted Array of String: [Angular, Css, Java, Python, Xml].
Let try the Arrays.sort of Object sorting. For example I will create a Employee class with basic attributes and will try to sort the object by using Arrays.sort method??
Here my Employee class, 
public class Employee {
private int id;
private String name;
private double experience;
private long salary;
private int rating;

public Employee(int id, String name, double experience, long salary, int rating) {
this.id = id;
this.name = name;
this.experience = experience;
this.salary = salary;
this.rating = rating;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getExperience() {
return experience;
}

public void setExperience(double experience) {
this.experience = experience;
}

public double getSalary() {
return salary;
}

public void setSalary(long salary) {
this.salary = salary;
}

public int getRating() {
return rating;
}

public void setRating(int rating) {
this.rating = rating;
}
}
Here test class to sort the  employee object using Arrays.sort().
public class EmployeeSort {
public static void main(String[] args) {
Employee[] employees = {
new Employee(5, "Antwaun", 5.5, 10000L, 1),
new Employee(2, "Rick", 32.5, 450000L, 3),
new Employee(1, "Richard", 50, 600000L, 4),
new Employee(3, "Corey", 12.5, 20000L, 2),
new Employee(4, "Chumlee", 10.5, 15000L, 5)
};
Arrays.sort(employees);
System.out.println("Sorting order of Employee by Id: "+Arrays.toString(employees));
}
}
 Lets run this main method, OOPS we are getting error, Runtime exception!!!

Exception in thread "main" java.lang.ClassCastException: com.example.monitoring.compare.Employee     cannot be cast to java.lang.Comparable
    at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
    at java.util.Arrays.sort(Arrays.java:1246)
    at com.example.monitoring.compare.EmployeeSort.main(EmployeeSort.java:14)


Process finished with exit code 1

Compiler says cannot case Employee to Comparable. So we need a Comparable type to sorting object.

Oh then how to cast our Employee class to Comparable java??. Yes here we go.. 

Java provide two interfaces to solve this issue.

  • Comparable 
  • Comparator
By implementing Comparable interface, we can override compareTo(Object o) to sort our custom Employee object. Lets modify our Employee class using Comparable interface.

Here Changes in Employee class, 

public class Employee implements Comparable<Employee>{
private int id;
private String name;
private double experience;
private long salary;
private int rating;

public Employee(int id, String name, double experience, long salary, int rating) {
this.id = id;
this.name = name;
this.experience = experience;
this.salary = salary;
this.rating = rating;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getExperience() {
return experience;
}

public void setExperience(double experience) {
this.experience = experience;
}

public double getSalary() {
return salary;
}

public void setSalary(long salary) {
this.salary = salary;
}

public int getRating() {
return rating;
}

public void setRating(int rating) {
this.rating = rating;
}

@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", experience=" + experience +
", salary=" + salary +
", rating=" + rating +
'}';
}

@Override
public int compareTo(Employee o) {
return (this.id-o.id);
}
}

Lets run the EmployeeSort,  

Output

Sorting order of Employee by Id: [Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}, Employee{id=7, name='Antwaun', experience=5.5, salary=10000, rating=1}, Employee{id=7, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=7, name='Chumlee', experience=10.5, salary=15000, rating=5}]

So now we sorted employee by their id so far so good. What if the scenarios come like of we to sort by employee experience, employee rating , employee salary, employee name or combination of these attributes. Do we need to create N number of classes to solve this??

No need, Java provider Comparable interface, by implementing this interface we can create combination of sorting logic for employee object depends on the scenarios. 

Comparator Interface provider compare(Object o1, Object o2) method, by overriding this method we can sort the objects.

I made some changes in Employee class, changes are below,

public static Comparator<Employee> nameComparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
return(o1.getName().compareTo(o2.getName()));
}
};

public static Comparator<Employee> salaryComparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
return (int) (o1.getSalary() - o2.getSalary());
}
};

public static Comparator<Employee> ratingComparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
return o1.getRating() - o2.getRating();
}
};

public static Comparator<? super Employee> experienceComparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
return (int) (o1.getExperience() - o2.getExperience());
}
};
We need use these comparator in EmployeeSort class, Changes in EmployeeSort class. 
Arrays.sort(employees, Employee.nameComparator);
System.out.println("Sorting order of Employee by Name: "+Arrays.toString(employees));

Arrays.sort(employees, Employee.salaryComparator);
System.out.println("Sorting order of Employee by Salary: "+Arrays.toString(employees));

Arrays.sort(employees, Employee.ratingComparator);
System.out.println("Sorting order of Employee by Rating: "+Arrays.toString(employees));

Arrays.sort(employees, Employee.experienceComparator);
System.out.println("Sorting order of Employee by Experience: "+Arrays.toString(employees));
Output:
Sorting order of Employee by Id: [Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}, Employee{id=3, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}, Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}]
Sorting order of Employee by Name: [Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}, Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}, Employee{id=3, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}]
Sorting order of Employee by Salary: [Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}, Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}, Employee{id=3, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}, Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}]
Sorting order of Employee by Rating: [Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}, Employee{id=3, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}, Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}, Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}]
Sorting order of Employee by Experience: [Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}, Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}, Employee{id=3, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}, Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}]

Here we all used Array of  object lets try the same with list. 

Here I added code in EmployeeSort class. And above output we can see all the order are ascending. For name displaying ascending order is good, but rating, experience  and salary we need descending order.

Comparator provider reversed() to reverse the sorting order by using that we can list them reverse. I have used it for rating, experience  and salary sorting.

Here added list of employee object in list. employee1-5 added in above snippet,

List<Employee> employeeList = new ArrayList<>();
employeeList.add(employee1);
employeeList.add(employee2);
employeeList.add(employee3);
employeeList.add(employee4);
employeeList.add(employee5);


Let we check output one by one
employeeList.sort(Employee.nameComparator);
System.out.println("Sorting order List of Employee by Name: "+employeeList);
Output
Sorting order List of Employee by Name: [Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}, Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}, Employee{id=3, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}]
Let order by salary, here I used reversed() method to get reverse order of salary.
employeeList.sort(Employee.salaryComparator.reversed());
System.out.println("Sorting order List of Employee by Salary: "+employeeList);
Output
Sorting order List of Employee by Salary: [Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}, Employee{id=3, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}, Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}]

Rating order,
employeeList.sort(Employee.ratingComparator.reversed());
System.out.println("Sorting order List of Employee by Rating: "+employeeList);

Output
Sorting order List of Employee by Rating: [Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}, Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}, Employee{id=3, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}]
Experience order,
employeeList.sort(Employee.experienceComparator.reversed());
System.out.println("Sorting order List of Employee by Experience: "+ employeeList);
Output:
Sorting order List of Employee by Experience: [Employee{id=1, name='Richard', experience=50.0, salary=600000, rating=4}, Employee{id=2, name='Rick', experience=32.5, salary=450000, rating=3}, Employee{id=3, name='Corey', experience=12.5, salary=20000, rating=2}, Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}, Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}]

Let see how to work with combination of multiple attribute comparison.  

public static Comparator<Employee> experienceAndSalaryComparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
int flag = (int) (o1.getExperience() -o2.getExperience());
if(flag == 0){
flag = (int) (o1.getSalary() - o2.getSalary());
}
return flag;
}
};
To check this scenario I made some changers in employee input, 

Employee employee1 = new Employee(5, "Antwaun", 5.5, 10000L, 1);
Employee employee2 = new Employee(2, "Rick", 32.5, 2000L, 3);
Employee employee3 = new Employee(1, "Richard", 50, 3000L, 4);
Employee employee4 = new Employee(3, "Corey", 32.5, 20000L, 2);
Employee employee5 = new Employee(4, "Chumlee", 10.5, 15000L, 5);
Lets call new comparator in EmployeeSort class,
employeeList.sort(Employee.experienceAndSalaryComparator.reversed());
System.out.println(employeeList);
Output:
[Employee{id=1, name='Richard', experience=50.0, salary=3000, rating=4}, Employee{id=3, name='Corey', experience=32.5, salary=20000, rating=2}, Employee{id=2, name='Rick', experience=32.5, salary=2000, rating=3}, Employee{id=4, name='Chumlee', experience=10.5, salary=15000, rating=5}, Employee{id=5, name='Antwaun', experience=5.5, salary=10000, rating=1}]

Yes almost we covered all the possible implementation on Comparable and comparator interface in java to sort the Object and primitive types.

Some common points above sorting,

  • Both compare() and compareTo() method returns are below
    • first argument is less than second argument return -1
    • first argument is equals to second argument return 0
    • first argument is greater than second argument return 1.
      • compare o1 first argument o2 second argument
      • compareTo this first argument o second argument
  • Comparable provide single way soring. Comparator provide multiple way of sorting

Thanks all.

No comments:

Post a Comment