Friday, September 10, 2021

Java Comparator Example for Custom Sorting Employee by Name, Age and Salary

In this tutorial, we will see the Java Comparator example to sort an Employee object by name, age, and salary. In order to sort Employee object on different criteria, we need to create multiple comparators e.g. NameComparator, AgeComparator, and SalaryComparator, this is known as custom sorting in Java. This is different from the natural ordering of objects, provided by the compareTo() method of java.lang.Comparable interface. Though both compare() and compareTo() method looks similar they are different in the sense that, former accepts one parameter, while later accepts two-parameter. Former compare passed object to the current object, on the other hand, compare() method compares two different objects passed to it.

You can create this comparator as a nested static class because they require access to private members of the class.

Once you create these Comparator implementations, all you need to do is to override compare() method accordingly e.g. in compare() method of NameComparator compare the name of two employees and return positive if the name of the first employee is greater than the second, and return negative if the name of the first employee is less than second and return zero if the name of both employees is equal.

By using Generics, you can avoid casting an object into the Employee inside compare() method, as shown in the example of this article. Once you have your Comparator classes ready, you can create a bunch of employees and sort them using any Comparator by passing List of employees and Comparator into Collections.sort(List, Comparator) method.

By the way, this Java example also implements equals(), hashcode(), compareTo(), and toString() method for Employee object, along with JUnit test for testing various Comparator implementation in file EmployeeTest.java.

And, If you are new to the Java world then I also recommend you go through The Complete Java MasterClass on Udemy to learn Java in a better and more structured way. This is one of the best and up-to-date courses to learn Java online.




Custom Sorting in Java using Comparator Example

Java Comparator Example Custom Sorting of Objects
Here is the complete code of creating different Comparator classes to compare objects on different attributes. In our example, we have an Employee object, which has fields like int id, String name, int salary, int age, and Date field to represent the date of joining. Our requirement is to sort a List of employees based upon their name, age, salary, and date of joining.






For this purpose, we have created four different comparator classes namely NameComparator, AgeComparator, SalaryComparator, and DOJComparator.

All classes implement java.util.Comparator interface but their compare() method are overridden differently e.g. first one compares the name of two employee object, second compare their salary, third compare their age and last one compares their age. 

When overriding compare() method, if you find an object, which overrides compareTo() then invoke that, for example for comparing String and Date we are invoking their compareTo() methods.



Files to run this Example
Employee.java
EmployeeTest.java

In order to run this program, simply copy-paste the following two classes into two different Java source files. Then name them according to the name of the public class e.g. Employee.java and EmployeeTest.java. You also need JUnit to run a test file, which tests the code you have written for implementing Comparator and overriding compare() method. 

You can run it on Eclipse or command prompt as per your convenience. If you are not using JUnit then you just put the test code inside the main method of any class and run it accordingly.

Employee.java
===============
import java.util.Comparator;
import java.util.Date;

/**
 *
 * @author
 */
public class Employee implements Comparable<Employee>{
    private int id;
    private String name;
    private int salary;
    private int age;
    private Date dateOfJoining;
  
    public static final Comparator<Employee> AgeComparator = new Comparator<Employee>(){

        @Override
        public int compare(Employee o1, Employee o2) {
            return o1.age - o2.age// This will work because age is positive integer
        }
      
    };
  
    public static final Comparator<Employee> SalaryComparator = new Comparator<Employee>(){

        @Override
        public int compare(Employee o1, Employee o2) {
            return o1.salary - o2.salary; // salary is also positive integer
        }
      
    };
  
    public static final Comparator<Employee> NameComparator = new Comparator<Employee>(){

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

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

    public Employee(int id, String name, int salary, int age, Date dateOfJoining) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.dateOfJoining = dateOfJoining;
    }

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

    }

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

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Employee other = (Employee) obj;
        if (this.id != other.id) {
            return false;
        }
        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
            return false;
        }
        if (this.salary != other.salary) {
            return false;
        }
        if (this.age != other.age) {
            return false;
        }
        if (this.dateOfJoining != other.dateOfJoining && (this.dateOfJoining == null || !this.dateOfJoining.equals(other.dateOfJoining))) {

            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 47 * hash + this.id;
        hash = 47 * hash + (this.name != null ? this.name.hashCode() : 0);
        hash = 47 * hash + this.salary;
        hash = 47 * hash + this.age;
        hash = 47 * hash + (this.dateOfJoining != null ? this.dateOfJoining.hashCode() : 0);
        return hash;
    }

}

EmployeeTest.java
==================
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 * Test class to test sorting of Employee object on different parameters.
 * each test method will test each Comparator implementation.
 * @author http://javarevisited.blogpot.com
 */
public class EmployeeTest {
  
 
    @Test
    public void testEmployeeSorting(){
        Employee e1 = new Employee(1, "A", 1000, 32, new Date(2011, 10, 1));
        Employee e2 = new Employee(2, "AB", 1300, 22, new Date(2012, 10, 1));
        Employee e3 = new Employee(3, "B", 10, 42, new Date(2010, 11, 3));
        Employee e4 = new Employee(4, "CD", 100, 23, new Date(2011, 10, 1));
        Employee e5 = new Employee(5, "AAA", 1200, 26, new Date(2011, 10, 1));
      
        List<Employee> listOfEmployees = new ArrayList<Employee>();
        listOfEmployees.add(e1);
        listOfEmployees.add(e2);
        listOfEmployees.add(e3);
        listOfEmployees.add(e4);
        listOfEmployees.add(e5);
        System.out.println("Unsorted List : " + listOfEmployees);
      
        Collections.sort(listOfEmployees);      //Sorting by natural order
        assertEquals(e1, listOfEmployees.get(0));
        assertEquals(e5, listOfEmployees.get(4));
      
        Collections.sort(listOfEmployees, Employee.NameComparator);
        assertEquals(e1, listOfEmployees.get(0));
        assertEquals(e4, listOfEmployees.get(4));
      
        Collections.sort(listOfEmployees, Employee.AgeComparator);
        assertEquals(e2, listOfEmployees.get(0));
        assertEquals(e3, listOfEmployees.get(4));
      
        Collections.sort(listOfEmployees, Employee.SalaryComparator);
        assertEquals(e3, listOfEmployees.get(0));
        assertEquals(e2, listOfEmployees.get(4));
      
        Collections.sort(listOfEmployees, Employee.DOJComparator);
        assertEquals(e3, listOfEmployees.get(0));
        assertEquals(e2, listOfEmployees.get(4));
    }
}



That's all on this Java Comparator example tutorial. We have learned how to create multiple Comparators in a class for comparing objects in different parameters. This allows you to sort objects in any custom order, depending upon business requirements. 

You can provide a custom Comparator is a nested static class because a Comparator is closely associated with the object it compares. 

This is one of the genuine usages of nested static classes in Java. It's good to provide natural ordering of objects via the compareTo() method and provide additional compare() methods for custom sorting of objects in Java. 

Always use Generics while overriding compare(), this will make your program as now your compare() will accept two Employee objects, instead of two java.lang.Object parameters.



Other Java Collection tutorials you may like
  • How to sort a Map by keys and values in Java? (tutorial)
  • How to sort an ArrayList in ascending and descending order in Java? (tutorial)
  • What is the difference between ArrayList and HashSet in Java? (answer)
  • What is the difference between TreeMap and TreeSet in Java? (answer)
  • What is the difference between HashMap and ConcurrentHashMap in Java? (answer)
  • The difference between HashSet and TreeSet in Java? (answer)
  • The difference between ArrayList and LinkedList in Java? (answer)
  • The difference between Vector and ArrayList in Java? (answer)

Thanks for reading this article so far. If you like this article then please share it with your friends and colleagues. If you have any questions or feedback then please drop a comment.

15 comments :

Unknown said...

Nice article , but there are not static nested class(NameComparator, AgeComparator, SalaryComparator and DOJComparator), I would say there are static anonymous class.

Unknown said...

Equals can not be simplier?

Sham said...

A small correction - Instead of "Though both compare() and compareTo() method looks similar they are different in a sense that, FORMER accept one parameter, while LATER accept two parameter."
, it should be former (compare())accept two parameters and later(compareTo()) accepts one parameter

Anonymous said...

How to run this program

Anurag said...

From Java 8 onwards you can use lambda expression instead of anonymous class to implement custom comparator. You can also use method reference and some useful methods like Comparator::comparing() to sort with multiple attributes.

For example, your code for sorting employees based upon salary can be re-written in Java 8 as follows :

Collections.sort(employees, Comparator.comparing(Employee::salary));

This will sort all employees on increasing order of salary, but if you want to sort in decreasing order, you can just do this :

Collections.sort(employees, Comparator.comparing(Employee::salary).reversed());

That's the power of Java 8, clear, concise and succint code.

Unknown said...

Nice article, but there are some mistakes in example. Syntax of compare() and compareTo() are not correct. For Example, Instead of public int compare(Employee o1, Employee o2) , It should be public int compare(Object o1, Object o2)
Please correct the example.
Instead of

Unknown said...

As Comparator is an interface, how we can create an object for Comparator using new Keyword.

public static final Comparator DOJComparator = new Comparator()

javin paul said...

@Chandra, look at closely mate, we are using Anonymous class, which means a class which implements Comparator interface and that's why we are able to create instance using new() keyword. The logic of implementation is always in the overridden compare() method.

Unknown said...

Great article. I never thought of using anonymous class comparator to solve many sorting criteria within pojo, thanks a bunch.

Anonymous said...

Hello Javin. Recently i was asked in interview. Suppose i have a list of heterogeneous objects e.g. Student,employee,person,etc. Each having a common property firstName, how to write a comparator for it and it should not do any instanceOf checking.

Unknown said...

Hello friends, I was asked a question in an interview regarding comparator interface, they ask me how to sort employee id & employee name using only one comparator class. you can't create two separate comparator class for id comparator & name comparator.Sorting must be customized sorting order. Please one body answer me.

Anonymous said...

Collections.sort(listOfEmployees, Employee.NameComparator);

can you explain the above code?
what happens when i call sort method, how does the flow goes

javin paul said...

@Anonymous, when you call the sort method, you give a list of employee and a Comparator to compare them. The Java uses a modified version of Mergesort to sort the list of employee and for comparing two employee it uses the Comparator you have provided.

javin paul said...

Hello @Kanhu, are they after a Comparator which first sort on ID and then on NAME? e.g. if two employee have same id then check their name? if yes then it's possible, you can do that in compare() method, just check if id is equal first if they are then check if name is equal then return 0 or 1 or -1 accordingly e.g.

if(obj1.id === obj2.id){
if(obj1.name.equals(obj2.name)){
return 0;
}else{
...
}
}else{
if(obj1.id > obj2.id){ return 1;}
else{
return -1;
}
}

Anonymous said...

I have question please clarify . When we use
Collections.sort(employees, Comparator.comparing(Employee::salary));
That means now we do not need inner classes like SalaryComparator that implement comparator interface. Also in this case it's just enough if employee classs implements Comparable interface right ?

Post a Comment