Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

in what way is a comparator superior to comparable?

"How will you sort collection of employee objects by its id or name". For that we can use two interfaces, i.e., Comparator and Comparable. seems this is one of the common interview questions

But I don't see a reason why I should use both for sorting employee objects

I have been thinking on what comparator accomplishes that Comparable cannot do. I understand that if the objects (instance variables that is compared upon) have natural ordering then comparable is the right choice. but if custom ordering is needed (eg string length) then one could write a comparator. my point here is comparator is only needed by the client if he wants to sort the data by some other criteria. For example, I would implement an Employee class to sort by id using comparable interface. but if the client wants to sort Employee objects by String(name), he would implement comparator either as a concrete class or anonymously in sorting. Is there anything I am missing here?

For example, In the following code, for the Person object, my compareTo method, compares the age and sort it In the compare method, I use String length (name of the person) for sorting. In theory, I could accomplish both in the compareTo method as I have implemented below.

lastly, are there any added benefits of one of the following over other I have implemented comparator in two ways 1. as a static method which is commented out 2. as anonymous object(?) in the main method which is commented out 3. make a new class that implements comparator and call the instance of that class in collections.sort() -- this I have not done here

(The commented-out parts of the code works. They are just different implementations)

mport java.util.Collections;
import java.util.Comparator;
import java.util.*;

public class PersonComparator implements Comparable{
    private String name;
    private int age;

    public PersonComparator(String name, int age) {
        this.name = name;
        this.age = age;
    }
@Override
public String toString() {
    return "name=" + name + ", age=" + age;
}

/*@Override
public int compareTo(Object obj) {
    if (!(obj instanceof PersonComparator)) {
        throw new ClassCastException("Invalid object");
    }
    PersonComparator p2 = (PersonComparator)obj;
    return this.age-p2.age;
}*/

/*Alternative CompareTo that checks for both age and name*/
 public int compareTo(Object obj) {
    if (!(obj instanceof PersonComparator)) {
        throw new ClassCastException("Invalid object");
    }
    PersonComparator p2 = (PersonComparator)obj;
    if (this.age!=p2.age){
        return this.age-p2.age;
    }
    else {
    return (this.name.length()-p2.name.length());
}
} 


/*public static Comparator nameLengthComparator 
= new Comparator() {


    @Override
    public int compare(Object obj1, Object obj2) {
        if (!(obj1 instanceof PersonComparator) || !(obj2 instanceof PersonComparator)){
            throw new ClassCastException("Invalid object");
        }
        else {
            PersonComparator p1 = (PersonComparator)obj1;
            PersonComparator p2 = (PersonComparator)obj2;
            return p1.name.length()-p2.name.length();
        }
}
};*/

 public static void main(String[] args){
     PersonComparator p1 = new PersonComparator("Alexander", 45);
     PersonComparator p2 = new PersonComparator("Pat", 27);
     PersonComparator p3 = new PersonComparator("Zacky", 45);
     PersonComparator p4 = new PersonComparator("Rake", 34);

     List<PersonComparator> list = new ArrayList<PersonComparator>();
     list.add(p1);
     list.add(p2);
     list.add(p3);
     list.add(p4);

     System.out.println("Before sorting "+ list);
     Collections.sort(list);
     //System.out.println("After sorting by age "+ list);
     //System.out.println("Before sorting "+ list);
     //Collections.sort(list, nameLengthComparator);
     System.out.println("After sorting by name length "+ list);
     /*Collections.sort(list, new Comparator<PersonComparator>() {
         @Override
            public int compare(PersonComparator p1, PersonComparator p2) {
                    return p1.name.length()-p2.name.length();
                }
        }
    );*/
     System.out.println("After sorting by name length "+ list);
 }

}

Thanks

like image 620
brain storm Avatar asked Aug 31 '13 01:08

brain storm


1 Answers

Comparable interface

The Comparable interface defines a type's natural ordering. Suppose you have a list of String or Integer objects; you can pass that list to

Collections.sort(list);

and you will have a sorted list. How? Because String and Integer both implement Comparable interface and the implementations of Comparable interface provide a natural ordering. Its like the class definition saying - "If you find a collection of objects of my type, order them according to the strategy I have defined in the compareTo method".

Now when you define your own type, you can define the natural ordering of the objects of your class by implementing the Comparable interface. See the Java documentation for more information on object ordering.

Comparator interface

The Comparator interface describes how to define custom strategies for object ordering. Suppose we have a simple Person type as below:

public class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Now, by implementing the Comparator interface, you can write different strategies to order the instances of your Person type. For example, consider the two strategies for ordering Person objects given below:

class StrategyOne implements Comparator<Person> {

    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().length() - p2.getName().length();
    }

}

class StrategyTwo implements Comparator<Person> {

    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }

}

Here, StrategyOne will order Person objects based on the length of their names, and StrategyTwo will order Person objects based on lexicographic ordering of their names.

The ways to implement Comparator

As you can see, the concrete strategy classes are stateless, hence all instances are functionally equivalent. So, we just need a single instance of any concrete strategy class. Thus, it should be a singleton. Using anonymous classes will create a new instance each time the call is executed. Consider storing the object in a private static final field and reusing it by using static factory methods to access them [Effective Java]. For example, you can reuse the above two concrete strategies as below:

class Strategies {
    private static final Comparator<Person> PERSON_NAME_LENGTH_COMPARATOR = new StrategyOne();

    private static final Comparator<Person> PERSON_NAME_LEXICAL_COMPARATOR = new StrategyTwo();

    public static Comparator<Person> personNameLengthComparator(){
         return  PERSON_NAME_LENGTH_COMPARATOR;
    }


    public static Comparator<Person> personNameLexicalComparator(){
         return  PERSON_NAME_LEXICAL_COMPARATOR;
    }
}

Summary

To summarize, the Comparable interface is used to define the natural ordering of a class, and the Comparator interface is used to define particular strategies for object ordering.

like image 117
Debojit Saikia Avatar answered Oct 13 '22 05:10

Debojit Saikia