Sunday, December 2, 2012

Comparator and Comparable in Java

Sorting in Java is easy if you are clear with its fundamentals. Two basic building blocks which java provides for sorting are Comparator and Comparable interfaces. Java designers have implemented these two interfaces in most of the API classes like String, Integer, BigDecimal, Date etc to provide default or in other words natural ordering of objects.

Collection classes also rely on the implementation of these interfaces to sort the collections of objects they are holding. Sorted collections classes like TreeMap or TreeSet can only contain collection of object which implement comparable or other wise we need to explicitly provide a comparator. If you try to save the collection of objects and don't provide a Comparator then TreeSet / TreeMap assumes that the elements implement Comparable. In case it is not - you will get a classCastException.
To do sorting on objects in non sorted collection classes like HashMap, Arraylist etc we need to use sot method of Collections utility class and provide a comparator to it as well. 

Following are some of the theoretical and logical differences in these two interfaces

Comparator Comparable
Is in java.util package which implicitly says that this is a utility to sort java objects. Is in java.lang package.
Used to implement customized ordering of objects.  Used to implement natural ordering of objects. It make the implementing class comparable (by default).
Has public int compare (Object o1, Object o2) method. Returns -ve, 0 or +ve integer based on whether first argument is <, = or > the second argument. Has public int compareTo(Object o1)
method. Returns -ve, 0 or +ve integer based on whether this object is <, = or > the specified object.
Used to compare two objects provided to it. Used to compare the 'this' reference of object with other specified object.
Objects which don't implement comparable can be used as keys in sorted collections structures like SortedMap (TreeMap) or SortedSet (TreeSet) only by specifying a comparator. Objects implementing comparable can be used as keys in sorted collections structures like SortedMap (TreeMap) or SortedSet (TreeSet).


In Summary if there is a natural or default way of sorting Object already exist during
development of Class than use Comparable like Employee can be sorted by there Id etc.
On the other hand if an Object can be sorted on multiple ways and client is specifying
on which parameter sorting should take place than use Comparator for example
Employee can again be sorted on name or department etc. Comparator implementation
can sort out this problem.


// Employee Class : default sorting is based employeeId.

public class Employee implements Comparable{

    private String employeeId;
    private String employeeFirstName;
    private String department;
    private String employeeLastName;
    
    public Employee(String empId, String firstName, String lastName, String dept) {
        this.employeeId = empId;
        this.employeeFirstName = firstName;
        this.employeeLastName = lastName;
        this.department = dept;
    }
    public String getEmployeeId() {
        return employeeId;
    }
    public void setEmployeeId(String employeeId) {
        this.employeeId = employeeId;
    }
    public String getEmployeeFirstName() {
        return employeeFirstName;
    }
    public void setEmployeeFirstName(String employeeFirstName) {
        this.employeeFirstName = employeeFirstName;
    }
    public String getDepartment() {
        return department;
    }
    public void setDepartment(String dept) {
        this.department = dept;
    }
    public String getEmployeeLastName() {
        return employeeLastName;
    }
    public void setEmployeeLastName(String employeeLastName) {
        this.employeeLastName = employeeLastName;
    } 
    public String toString() {
        return this.employeeId + " : " + this.employeeFirstName + " "
                + this.employeeLastName + " : " + this.department;
    }
        //overrided method to do the default sorting based on employeeId

        public int compareTo(Employee emp) {
        int thisEmployeeId = Integer.valueOf(this.employeeId).intValue();
        int anotherEmployeeId = Integer.valueOf(emp.employeeId).intValue();
        if (thisEmployeeId &gt; anotherEmployeeId) {
            return 1;
        } else if (thisEmployeeId &lt; anotherEmployeeId) {
            return -1;
        }
        return 0;
               // Dont use difference of integers to decide output of compareTo method
               // as result of integer subtraction can overflow but if you are sure that both
               // operands are positive then its one of the quickest way to compare two objects.
               // return thisEmployeeId - anotherEmployeeId;
        }
}

//Comparator which does the sorting on Employee first name
public class EmployeeNameComparator implements Comparator {
    public int compare(Employee emp1, Employee emp2) {
        return emp1.getEmployeeFirstName().compareTo(emp2.getEmployeeFirstName());
    }
}

public class ComparatorbleTest {
    
    public static void main(String... args){
        Employee emp = new Employee("67899", "Richard", "Bar", "Artist");
        Employee emp1 = new Employee("12345", "Michelle", "Steven", "Acting");
        Employee emp2 = new Employee("78900", "Alex", "Fox", "Hollywood");
        Employee emp3 = new Employee("69875", "Salman", "Khan", "Bollywood");
        Employee emp4 = new Employee("09455", "Gangnam", "Style", "Singing");
        List empList = new ArrayList();
        empList.add(emp);
        empList.add(emp1);
        empList.add(emp2);
        empList.add(emp3);
        empList.add(emp4);
                //since the object implements comparable it will by default sort
                // objects in the list by employee id.
        Collections.sort(empList);
        for(Employee employee : empList){
            System.out.println(employee.toString());
        }
        System.out.println("");
                // to do a customized sort (first name) on employee list we 
                // need to provide a corresponding customized comparator. 
        Collections.sort(empList, new EmployeeNameComparator());
        for(Employee employee : empList){
            System.out.println(employee.toString());
        }
    }
}

 

No comments: