Friday, January 24, 2014

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

In this tutorial, we will see Java Comparator example to sort an Employee object by name, age and salary. In order to sort Employee object on different criterion, we need to create multiple comparators e.g. NameComparator, AgeComparator and SalaryComparator, this is known as custom sorting in Java. This is different then natural ordering of object, provided by compareTo() method of java.lang.Comparable interface. Though both compare() and compareTo() method looks similar they are different in a sense that, former accept one parameter, while later accept two parameter. Former compare passed object to current object, on the other hand compare() method compares two different object passed to it. You can create this comparator as nested static class, because they require access of private members of 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 name of two employee and return positive if name of first employee is greater than second, return negative if name of first employee is less than second and return zero if name of both employees are equal. By using Generics, you can avoid casting of object into Employee inside compare() method, as shown in example of this article. Once you have your Comparator classes ready, you can create a bunch of employee 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.


Custom Sorting in Java using Comparator Example

Java Comparator Example Custom Sorting of Objects
Here is 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 date of joining. Our requirement is to sort a List of employee 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 implements java.util.Comparator interface but their compare() method are overridden differently e.g. first one compares name of two employee object, second compares their salary, third compare their age and last one compare 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 following two classes in two different Java source file. Then name them according to name of public class e.g. Employee.java and EmployeeTest.java. You also need JUnit to run test file, which test 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 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 Comparator in a class for comparing object in different parameters. This allows you to sort object in any custom order, depending upon business requirement. You can provide custom Comparator as nested static class, because a Comparator is closely associated with the object it compare. This is one of the genuine usage of nested static class in Java. It's good to provide natural ordering of object via compareTo() method and provide additional compare() methods for custom sorting of object in Java. Always use Generics while overriding compare(), this will make your program as now your compare() will  accept two Employee object, instead of two java.lang.Object parameters.

6 comments :

Piotr Chlebda said...

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

Tamás Kőrössy 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...

good explanation...

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.

Post a Comment