brain storm brain storm - 1 month ago 8
Java Question

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

Answer

Comparable interface:

The Comparable interface defines a Type's natural ordering. Suppose you have a list of Strings or Integers, you can pass that list to

Collections.sort(list);

and you will have a sorted list. How? Because String and Integer both implements 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 Comparable interface. Here you will find more information on object ordering.

Comparator interface:

The comparator interface defines a "Strategy" interface using which you can write your 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 Comparator interface you can write different strategies to order the instances of your "Person" type. For example, consider the below two Strategies of Person ordering:

class StartegyOne implements Comparator<Person> {

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

}

class StartegyTwo implements Comparator<Person> {

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

}

Here the strategy "StrategyOne" will order the Persons based on the length of their names and "StrategyTwo" will order Persons 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. For example, you can reuse the above two concrete strategies as below :

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

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

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


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

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