Thursday, June 30, 2011

Sorting objects with more than one attribute

So the problem goes like this, We have a list of employee objects and you want to sort them first according to name and if same then according to Date of joining.

So we have two solutions to handle this situation one using Comparable interface implementation and other using ComparatorChain class from apache collections api.
First Solution:
Take a closer look at the compareTo method. We are first comparing the name if same then comparing the salary and if salary is also same then we are comparing the date of joining.
Below is our Employee_ class which is slightly different from Employee class

package blog.javaespresso.comparable.example.bean;

import java.util.Date;

package blog.javaespresso.comparable.example.bean;

import java.util.Date;

/**
 * @author Dharmvir Singh
 *
 */
public class Employee_ implements Comparable<Employee_>{
 public Employee_(){}
 public Employee_(int empId,String name,double salary,Date dateOfJoining){
  this.empId = empId;this.name =  name;
  this.salary =  salary;this.dateOfJoining = dateOfJoining;
 }
 private int empId;
 private String name;
 private double salary;
 private Date dateOfJoining;
 @Override
 public int compareTo(Employee_ o) {
  if(o == null){
   throw new NullPointerException("compareTo: Argument passed is null");
  }
  if(this.getClass().equals(o.getClass())){
   Employee_ e = (Employee_) o;
   int i = this.getName().compareTo(e.getName());
   // if name is same then compare date of joinings
   if(i==0){
    return this.getDateOfJoining().compareTo(o.getDateOfJoining());
   }
   return i;
  }else{
   throw new ClassCastException("compareTo: Objects are not comparable");
  }
 }
 // setters and getters
 public int getEmpId() {return empId;}
 public void setEmpId(int empId) {this.empId = empId;}
 public String getName() {return name;}
 public void setName(String name) {this.name = name;}
 public double getSalary() {return salary;}
 public void setSalary(double salary) {this.salary = salary;}
 public Date getDateOfJoining() {return dateOfJoining;}
 public void setDateOfJoining(Date dateOfJoining) {this.dateOfJoining = dateOfJoining;}
}
}
Now let's take a look at the test class which is same as we saw in the article: Comparable and Comparator interfaces-Part 1
TestEmployeeSorting Class

Now can you identify the problem with the above class, Yes every time our conditions of sorting will change, Employee_ class has to change and it is never a good idea to modify the code.

Below we improve our solution by using ComparatorChain class from apache commons collection framework.

Second Solution:
we do not touch Employee class at all means don't let it implement either Comparable or Comparator class.
So here is our EmployeePojo class

After that create your comparators, Here we create two comparators one for Name and other for date of joining.
EmployeeNameComparator, EmployeeDOJComparator

Below is the code for Test class for doing sorting using ComparatorChain class.

package blog.javaespresso.comparator.example.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.collections.comparators.ComparatorChain;

import blog.javaespresso.comparable.example.bean.Employee;
import blog.javaespresso.comparator.example.customcomparator.*;

public class TestEmployeeChainSorting {
 public static void main(String[] args) {
  Employee e1 = new Employee(1,"A",20000.00,new Date(2010,12,11));
  Employee e2 = new Employee(2,"A",22000.00,new Date(2009,12,11));
  Employee e3 = new Employee(3,"A",10000.00,new Date(1990,12,11));
  Employee e4 = new Employee(4,"F",19000.00,new Date(2001,12,11));
  Employee e5 = new Employee(5,"E",24000.00,new Date(2006,12,11));
  List<Employee> list = new ArrayList<Employee>();
  list.add(e1);list.add(e2);list.add(e3);list.add(e4);list.add(e5);

  // PRINT AFTER SORTED BY NAME
  System.out.println("BEFORE SORTING");
  System.out.println("===============================================================");
  System.out.println("ID  Name  Salary  Date Of Joining");
  System.out.println("===============================================================");
  for (Employee employee : list) {
   System.out.println(employee.getEmpId()+"\t\t "+employee.getName()
     +"\t\t"+employee.getSalary()+"\t\t "+
     employee.getDateOfJoining().getDay()+"-"+employee.getDateOfJoining().getMonth()
     +"-"+employee.getDateOfJoining().getYear());
  }
  ComparatorChain chain =  new ComparatorChain();
  chain.addComparator(new EmployeeNameComparator());
  chain.addComparator(new EmployeeDOJComparator());
  // sorting the Employee object

  Collections.sort(list,chain);

  // PRINT AFTER SORTED BY AGE
  System.out.println("BEFORE SORTING");
  System.out.println("===============================================================");
  System.out.println("ID  Name  Salary  Date Of Joining");
  System.out.println("===============================================================");
  for (Employee employee : list) {
   System.out.println(employee.getEmpId()+"\t\t "+employee.getName()
     +"\t\t"+employee.getSalary()+"\t\t "+
     employee.getDateOfJoining().getDay()+"-"+employee.getDateOfJoining().getMonth()
     +"-"+employee.getDateOfJoining().getYear());
  }
 }
}

Important part in above code is: You create a ComparatorChain object and whatever comparators you want to apply, simply add them to it and then sort the collection using this ComparatorChain object.
ComparatorChain chain =  new ComparatorChain();
  chain.addComparator(new EmployeeNameComparator());
  chain.addComparator(new EmployeeDOJComparator());
  // sorting the Employee object
  Collections.sort(list,chain);

Related articles:
Comparable and Comparator interfaces-Part 1
Comparable and Comparator interfaces-Part 2

5 comments:

  1. In the first solution. The compareTo method can use Employee_ as parameter and there would be no need of castin or checking the class, since the class implements Comparable.

    ReplyDelete
  2. A very good and informative solution. Thumbs Up!

    ReplyDelete
  3. Really excellent and very wonderful. Thanks Dharmvir.

    ReplyDelete