Comparable and Comparator -Part 4



Comparator Interface

As discussed in part 3 that when you have more than one sorting criteria then it is not proper to change compareTo() method again and again. In these case you need to use Comparator interface.

Comparator interface is present in java.util package and have two methods named compare() and equals().

Unlike Comparable which  ideally should be used to sort things according to their natural order, Comparator can be used to sort things in any order you want.

Like compareTo(), compare() returns either a negative integer, zero, or a positive integer.

According to JavaDocs, if o1 and o2 are two objects, then if compare(o1,o2) :

1) returns a negative integer, it means o1 < o2.
2) returns a positive integer, it means o2< o1
3) returns 0 then o1==o2.

and

1) If o1 and o2 are not comparable then compare() should throw ClassCastException.


2) Unlike compareTo(), contract of compare() does not say anything about when o1 or o2 or both are null. So its upto you how you tackle null in your code for comparing objects.

Note : In Java 7 docs following statements have been added regarding null handling by compare():

Unlike Comparable, a comparator may optionally permit comparison of null arguments, while maintaining the requirements for an equivalence relation.

and

NullPointerException - if an argument is null and this comparator does not permit null arguments

Like Comparable, Comparator also have two versions:

1) Non Generic form (pre Java SE 5)

2) Generic form (Java SE 5 onwards)

Lets see the use of Comparator.

Now this time we are going to sort our employees according to their names and salary also in addition to sort them according to their ids.

Here I am showing generic version only and also use of generics is a good coding practice.

Employee.java
public class Employee implements Comparable<Employee>{
   private int empId;
   private String name;
   private  String city;
   private double salary;

  @Override
   public int compareTo(Employee obj){

         if(obj == null){
             throw new NullPointerException("Object passed is null!");
        }

        int id = obj.getEmpId();
      
        if(this.getEmpId() < id)  
            return -1;
       else if(this.getEmpId() > id )
            return 1;
       else
            return 0;
   }

   public void setEmpId(int id){  
        this.empId=id;
   }
 
   public int getEmpId(){  
      return empId;  
   }
   
   public void setName(String name){  
       this.name=name;
   }
 
   public String getName(){  
      return name;  
   }
  
   public void setCity(String city){  
      this.city=city;
   }
 
   public String getCity(){  
     return city;  
   }

  public void setSalary(double sal){  
    this.salary=sal;
  }
 
  public double getSalary(){  
    return salary;  
  }
}

Note that here you are still using Comparable (and compareTo()) for sorting employees by their ids (treating it as natural order). But for additional sorting criteria  (names and salary) you will use Comparator as shown below:

SalaryComparator.java
import java.util.Comparator;

public class SalaryComparator implements Comparator<Employee>{

  public int compare(Employee e1,Employee e2){

     /*Handling null*/
      if(e1==null|| e2==null){
          throw new NullPointerException("Employess cannot be null!");
      }

     double sal1=e1.getSalary();
     double sal2=e2.getSalary();

     if(sal1 < sal2)
        return -1;
    else if( sal1 > sal2)
        return 1;
    else
       return 0;
  }
}

NamesComparator.java
import java.util.Comparator;

public class NamesComparator implements Comparator<Employee>{

public int compare(Employee e1,Employee e2){
     
      /*Handling null*/
       if(e1==null|| e2==null){
            throw new NullPointerException("Employess cannot be null!");
       }

       String name1=e1.getName();
       String name2=e2.getName();
       return name1.compareTo(name2);
}
}

EmployeeComparision.java
public class EmployeeComparision{
 
    public static void main(String args[]){
  
       NamesComparator namesComp= new NamesComparator();
       SalaryComparator salComp= new SalaryComparator();
     
        Employee emp1 = new Employee();      
        emp1.setEmpId(1);
        emp1.setName("Mr. X");
        emp1.setCity("NY");
        emp1.setSalary(45000.0);
      
        Employee emp2 = new Employee();      
        emp2.setEmpId(2);
        emp2.setName("Mr. Y");
        emp2.setCity("New Delhi");
        emp2.setSalary(35000.0);
     
        /* Sorting in ascending order of their ids.*/
       System.out.println("Employees in acsending order(according to their ids):");
        if(emp1.compareTo(emp2) < 0) {      
            System.out.println("Emp1 < Emp2");
     
        } else if(emp1.compareTo(emp2) > 0) {      
            System.out.println("Emp2 < Emp1");
     
        } else if(emp1.compareTo(emp2) == 0) {      
            System.out.println("Both employees have same Id: emp1==emp2");      
        }
      
        /*Sorting in ascending order of their salary.Note the way in which compare() is called.*/
        System.out.println("Sorting in ascending order of their salary:");

        if(salComp.compare(emp1,emp2) < 0) {      
            System.out.println("Emp1 < Emp2");
         }
        else if(salComp.compare(emp1,emp2) > 0) {      
            System.out.println("Emp2 < Emp1");
        }
        else if(salComp.compare(emp1,emp2) == 0) {      
            System.out.println("Both employees have same salary: emp1==emp2");      
        }

       /*Sorting in ascending order of their names*/
        System.out.println("Sorting in ascending order of their names:");
      
        if(namesComp.compare(emp1,emp2) < 0) {      
            System.out.println("Emp1 < Emp2");
       }
       else if(namesComp.compare(emp1,emp2) > 0) {      
            System.out.println("Emp2 < Emp1");
       }
      else if(namesComp.compare(emp1,emp2) == 0) {      
            System.out.println("Both employees have same name: emp1==emp2");      
       }
    }

}

In above code we have created two comparators which implements comparision logic - SalaryComparator (for comparision by salary) and NamesComparator (for comparision by names). Then in the EmployeeComparision class, when we need to compare employees by salary we compared them as:

salComp.compare(emp1,emp2)   //using salary Comparator

and for names comparision we used:

namesComp.compare(emp1,emp2)  //using names Comparator

Note that for writing code for comparision by salary and names, we do not need to change our Employee class.

Also note that in NamesComparator class above we have used following statement in compare() :

return name1.compareTo(name2);

Here we comparing two strings using compareTo(). This is because java.lang.String class implements Comparable<String>. That's why we can use compareTo() here.

Using Arrays.sort()  

As we used Arrays.sort() with Comparable in part 3, we can also use it with Comparator interface. The following changed  EmployeeComparision class shows this:

EmployeeComparision.java
import java.util.Arrays;

public class EmployeeComparision{
 
    public static void main(String args[]){
     
        NamesComparator namesComp= new NamesComparator();
        SalaryComparator salComp= new SalaryComparator();
            
        Employee emp1 = new Employee();      
        emp1.setEmpId(3);
        emp1.setName("Mr. A");
        emp1.setCity("NY");
        emp1.setSalary(45000.0);
      
        Employee emp2 = new Employee();      
        emp2.setEmpId(2);
        emp2.setName("Mr. B");
        emp2.setCity("Los Angeles");
        emp2.setSalary(35000.0);
     
        Employee emp3 = new Employee();      
        emp3.setEmpId(1);
        emp3.setName("Mr. C");
        emp3.setCity("Chicago");
        emp3.setSalary(35000.0);
      
        Employee emp4 = new Employee();      
        emp4.setEmpId(4);
        emp4.setName("Mr. D");
        emp4.setCity("Los Angeles");
        emp4.setSalary(25000.0);
      
        Employee emp5 = new Employee();      
        emp5.setEmpId(5);
        emp5.setName("Mr. E");
        emp5.setCity("Boston");
        emp5.setSalary(30000.0);
      
        /* An array containing Employees */
        Employee [] empArray = new Employee[5];
        empArray[0]=emp1;
        empArray[1]=emp2;
        empArray[2]=emp3;
        empArray[3]=emp4;
        empArray[4]=emp5;
      
        /*Sorting in ascending order of their Ids.*/
      
        System.out.println("Employees in acsending order(according to their ids):");
     
        Arrays.sort(empArray);

        for(int i=0;i<=4;i++){
        System.out.println(empArray[i].getName()+", ");
      
        }  
      
        /*Sorting in ascending order of their Salary.Note the way in which sort() is called.*/
        System.out.println("Sorting in ascending order of their salary:");
      

        Arrays.sort(empArray, salComp);

        for(int i=0;i<=4;i++){
          System.out.println(empArray[i].getName()+", ");
        }      
  
       /*Sorting in ascending order of their Names.*/
        System.out.println("Sorting in ascending order of their names:");
      
          Arrays.sort(empArray,namesComp);

        for(int i=0;i<=4;i++){
           System.out.println(empArray[i].getName()+", ");
        }
    }
}


Note that here you are using overloaded version of Arrays.sort() which also accepts comparator object as one of its arguments:

Arrays.sort(empArray,namesComp);

Using Collections.sort()

Similar to Arrays.sort() we can also use overloaded version of Collections.sort() with following changes:

EmployeeComparision.java
     /* A List containing Employees */
        List<Employee> empList = new ArrayList<Employee>();
        empList.add(emp1);
        empList.add(emp2);
        empList.add(emp3);
        empList.add(emp4);
        empList.add(emp5);
      
      /*Sorting these employees to arrange them in ascending order of their ids.*/
       System.out.println("Employees in acsending order(according to their ids):");
     
        Collections.sort(empList);

        for(int i=0;i<empList.size();i++){
        System.out.println(empList.get(i).getName()+", ");
        }
      
        /*Sorting in ascending order of their Salary.Note the way in which sort() is called.*/
      
        System.out.println("Sorting in ascending order of their salary:");
      
        Collections.sort(empList,salComp);

        for(int i=0;i<empList.size();i++){
        System.out.println(empList.get(i).getName()+", ");
        }
      
       /*Sorting in ascending order of their Names.Note the way in which sort() is called.*/
      
        System.out.println("Sorting in ascending order of their names:");
      
        Collections.sort(empList,namesComp);

        for(int i=0;i<empList.size();i++){
        System.out.println(empList.get(i).getName()+", ");
      
        }       







I would like to know your comments and if you liked the article then please share it on social networking buttons.


1 comment:

  1. Great explanation........thanks helped a lot in understanding the speration

    ReplyDelete