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

Comparable and Comparator interfaces-Part 2

We have already seen the Comparable interface at work now let us see how to create comparators.

We will use the same Employee class.But without any Comparable interface implmentation. Now we have EmployeePojo class listed below:

package blog.javaespresso.comparable.example.bean;

import java.util.Date;

public class EmployeePojo {
 public EmployeePojo(){}
 public EmployeePojo(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;

 // 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;}
}

So objective is we need to display a report and report has options of sorting the employees either by name or date of joining.
take a look at the code listings:
Employee Name Comparator Class
package package blog.javaespresso.comparator.example.customcomparator;

import java.util.Comparator;
import blog.javaespresso.comparable.example.bean.EmployeePojo;

public class EmployeeNameComparator implements Comparator<EmployeePojo>{

 @Override
 public int compare(EmployeePojo e1, EmployeePojo e2) {
  if(e1 == null || e2 == null){
   throw new NullPointerException("compareTo: Argument passed is null");
  }
   return e1.getName().compareTo(e2.getName());
 }

}
Employee Date Of Joining Comparator Class
package package blog.javaespresso.comparator.example.customcomparator;

import java.util.Comparator;
import blog.javaespresso.comparable.example.bean.EmployeePojo;

public class EmployeeDOJComparator implements Comparator<EmployeePojo>{

 @Override
 public int compare(EmployeePojo e1, EmployeePojo e2) {
  if(e1 == null || e2 == null){
   throw new NullPointerException("compareTo: Argument passed is null");
  }
   return new Integer(e1.getDateOfJoining().compareTo(e2.getDateOfJoining()));
 }

}
Employee Comparator sorting test class:
We first create some employee objects and then first we sort them using name comparator and then using DOJ comparator
package blog.javaespresso.comparator.example.test;

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

import blog.javaespresso.comparable.example.bean.EmployeePojo;
import blog.javaespresso.comparator.example.customcomparator.EmployeeDOJComparator;
import blog.javaespresso.comparator.example.customcomparator.EmployeeNameComparator;

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

  Collections.sort(list,new EmployeeNameComparator());
  // PRINT AFTER SORTED BY NAME
  System.out.println("AFTER SORTED BY NAME");
  System.out.println("===============================================================");
  System.out.println("ID  Name  Salary  Date Of Joining");
  System.out.println("===============================================================");
  for (EmployeePojo 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());
  }

  // sorting the EmployeePojo object

  Collections.sort(list,new EmployeeDOJComparator());

  // PRINT AFTER SORTED BY AGE
  System.out.println("AFTER SORTED BY DATE OF JOINING");
  System.out.println("===============================================================");
  System.out.println("ID  Name  Salary  Date Of Joining");
  System.out.println("===============================================================");
  for (EmployeePojo 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());
  }
 }
}

Comparable and Comparator interfaces-Part 1
Part-3:Sorting objects with more than one attribute

Comparable and Comparator interfaces-Part 1

I have tried to make the article to be detailed and at the same time to the point.
 Comparable: It is an interface.If your class implements it object of your class can be compared with other objects of the same class. To compare you must implement the compareTo method in your class. compareTo must be consistent with the equals method (but not mandatory). For further details please refer to Comparable documentation.
    compareTo(Object o): Can be called like e1.compareTo(e2). Implementor should ensure following things:
  1. It should return Negative integer (if e1 < e2), 0 (if e1 = e2), Positive integer (if e1 > e2)
  2. It should throw ClassCastException if object types of e1 and e2 are not comparable
  3. It should throw NullPointerException if e2 passed is null
 Comparator: It is again an interface only. It is also used to provide ordering but you need not modify the class to provide the ordering to it. It has a compare(Object e1, Object e2). You create a separate class which implements the Comparator interface and implement the compare method
which does the actual work.
    compare(Object e1, Object e2): Can be called like compare(e1, e2)
  1. It should return Negative integer (if e1 < e2), 0 (if e1 = e2), Positive integer (if e1 > e2)
  2. It should throw ClassCastException if object types of e1 and e2 are not comparable
  3. It should throw NullPointerException if either e1 or e2 or both passed are null

Following are some classes in Java which implement comparable interface:
Note:
ClassNatural Ordering that the class has
Byte, Short, Integer, Long, Float, Double, BigInteger, BigDecimalSigned numerical
CharacterUnsigned numerical
BooleanBoolean.True > Boolean.False
StringLexographic
DateChronological
FileSystem-dependent lexicographic path name
CollationKeyLocale-specific lexicographic
Other classes implementing Comparable interface are URI, Charset, ByteBuffer, ShortBuffer, CharBuffer, IntBuffer, FloatBuffer, DoubleBuffer, LongBuffer

Example: Comparable
We has an Employee class which we need to store in a list in the ascending order of their name to display on the screen. Here goes the code for Employee class and Test class to check the ordering.

package blog.javaespresso.comparable.example.bean;

import java.util.Date;
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;
   return this.getName().compareTo(e.getName());
  }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 us see the test class:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import blog.javaespresso.comparable.example.bean.Employee;

public class TestEmployeeSorting {
 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 BEFORE SORTING
  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());
  }

  // sorting the Employee object
  Collections.sort(list);
  // PRINT AFTER SORTING
  System.out.println("\n\nAFTER 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());
  }
 }
}

To sort in the descending order of name, you just need to call reverse(List list) method of Collections class after doing sorting. For example,

Collections.sort(list);
Collections.reverse(list);
  // PRINT AFTER SORTING
  System.out.println("\n\nAFTER SORTING");

Related Articles
Comparable and Comparator interfaces-Part 2
Sorting objects with more than one attribute