[java] When to use Comparable and Comparator

I have a list of objects I need to sort on a field, say Score. Without giving much thought I wrote a new class that implements Comparator, that does the task and it works.

Now looking back at this, I am wondering if I should have instead have the my class implement Comparable instead of creating a new class that implements Comparator. The score is the only field that the objects will be ordered on.

  1. What I have done acceptable as a practice?

  2. Is the right approach "First have the class implement Comparable (for the natural ordering) and if an alternative field comparison is required, then create a new class that implements Comparator" ?

  3. If (2) above is true, then does it mean that one should implement Comparator only after they have the class implement Comparable? (Assuming I own the original class).

This question is related to java comparator comparable

The answer is


There had been a similar question here: When should a class be Comparable and/or Comparator?

I would say the following: Implement Comparable for something like a natural ordering, e.g. based on an internal ID

Implement a Comparator if you have a more complex comparing algorithm, e.g. multiple fields and so on.


Comparable should be used when you compare instances of the same class.

Comparator can be used to compare instances of different classes.

Comparable is implemented by the class which needs to define a natural ordering for its objects. For example, String implements Comparable.

In case a different sorting order is required, then, implement comparator and define its own way of comparing two instances.


Very simple approach is to assume that the entity class in question be represented in database and then in database table would you need index made up of fields of entity class? If answer is yes then implement comparable and use the index field(s) for natural sorting order. In all other cases use comparator.


I would say:

  • if the comparison is intuitive, then by all means implement Comparable
  • if it is unclear wether your comparison is intuitive, use a Comparator as it's more explicit and thus more clear for the poor soul who has to maintain the code
  • if there is more than one intuitive comparison possible I'd prefer a Comparator, possibly build by a factory method in the class to be compared.
  • if the comparison is special purpose, use Comparator

  • If at the moment of writing the class you have only one use case of sorting use Comparable.
  • Only when you have more than one strategy of sorting implement a Comparator.

Comparable:
Whenever we want to store only homogeneous elements and default natural sorting order required, we can go for class implementing comparable interface.

Comparator:
Whenever we want to store homogeneous and heterogeneous elements and we want to sort in default customized sorting order, we can go for comparator interface.


I have been asked sorting of a definite range of numbers in better than nlogn time in one of interview. (Not using Counting sort)

Implementing Comparable interface over an object allows implicit sorting algos to use overridden compareTo method to order sort elements and that would be linear time.


Use Comparable:

  • if the object is in your control.
  • if the comparing behaviour is the main comparing behaviour.

Use Comparator :

  • if the object is outside your control and you cannot make them implement Comparable.
  • when you want comparing behaviour different from the default (which is specified by Comparable) behaviour.

If sorting of objects needs to be based on natural order then use Comparable whereas if your sorting needs to be done on attributes of different objects, then use Comparator in Java.

Main differences between Comparable and Comparator:

+------------------------------------------------------------------------------------+
¦               Comparable                ¦                Comparator                ¦
¦-----------------------------------------+------------------------------------------¦
¦ java.lang.Comparable                    ¦ java.util.Comparator                     ¦
¦-----------------------------------------+------------------------------------------¦
¦ int objOne.compareTo(objTwo)            ¦ int compare(objOne, objTwo)              ¦
¦-----------------------------------------+------------------------------------------¦
¦ Negative, if objOne < objTwo            ¦ Same as Comparable                       ¦
¦ Zero,  if objOne == objTwo              ¦                                          ¦
¦ Positive,  if objOne > objTwo           ¦                                          ¦
¦-----------------------------------------+------------------------------------------¦
¦ You must modify the class whose         ¦ You build a class separate from to sort. ¦
¦ instances you want to sort.             ¦ the class whose instances you want       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Only one sort sequence can be created   ¦ Many sort sequences can be created       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Implemented frequently in the API by:   ¦ Meant to be implemented to sort          ¦
¦ String, Wrapper classes, Date, Calendar ¦ instances of third-party classes.        ¦
+------------------------------------------------------------------------------------+

Use Comparable if you want to define a default (natural) ordering behaviour of the object in question, a common practice is to use a technical or natural (database?) identifier of the object for this.

Use Comparator if you want to define an external controllable ordering behaviour, this can override the default ordering behaviour.


My need was sort based on date.

So, I used Comparable and it worked easily for me.

public int compareTo(GoogleCalendarBean o) {
    // TODO Auto-generated method stub
    return eventdate.compareTo(o.getEventdate());
}

One restriction with Comparable is that they cannot used for Collections other than List.


The following points help you in deciding in which situations one should use Comparable and in which Comparator:

1) Code Availabilty

2) Single Versus Multiple Sorting Criteria

3) Arays.sort() and Collection.sort()

4) As keys in SortedMap and SortedSet

5) More Number of classes Versus flexibility

6) Interclass comparisions

7) Natural Order

For more detailed article you can refer When to use comparable and when to use comparator


If you own the class better go with Comparable. Generally Comparator is used if you dont own the class but you have to use it a TreeSet or TreeMap because Comparator can be passed as a parameter in the conctructor of TreeSet or TreeMap. You can see how to use Comparator and Comparable in http://preciselyconcise.com/java/collections/g_comparator.php


Comparable is the default natural sorting order provided for numerical values are ascending and for strings are alphabetical order. for eg:

Treeset t=new Treeset();
t.add(2);
t.add(1);
System.out.println(t);//[1,2]

Comparator is the custom sorting order implemented in custom myComparator class by overriding a compare method for eg:

Treeset t=new Treeset(new myComparator());
t.add(55);
t.add(56);
class myComparator implements Comparator{
public int compare(Object o1,Object o2){
//Descending Logic
}
}
System.out.println(t);//[56,55]

Comparator does everything that comparable does, plus more.

| | Comparable | Comparator ._______________________________________________________________________________ Is used to allow Collections.sort to work | yes | yes Can compare multiple fields | yes | yes Lives inside the class you’re comparing and serves | | as a “default” way to compare | yes | yes Can live outside the class you’re comparing | no | yes Can have multiple instances with different method names | no | yes Input arguments can be a list of | just Object| Any type Can use enums | no | yes

I found the best approach to use comparators as anonymous classes as follows:

private static void sortAccountsByPriority(List<AccountRecord> accounts) {
    Collections.sort(accounts, new Comparator<AccountRecord>() {

        @Override
        public int compare(AccountRecord a1, AccountRecord a2) {
            return a1.getRank().compareTo(a2.getRank());
        }
    });
}

You can create multiple versions of such methods right inside the class you’re planning to sort. So you can have:

  • sortAccountsByPriority
  • sortAccountsByType
  • sortAccountsByPriorityAndType

    etc...

Now, you can use these sort methods anywhere and get code reuse. This gives me everything a comparable would, plus more ... so I don’t see any reason to use comparable at all.


If you need natural order sorting -- User Comparable IF you need Custom Order Sorting - Use Comparator

Example:

Class Employee{
private int id;
private String name;
private String department;
}

Natural order Sorting would be based on id because it would be unique and custom order sortin g would be name and department.

Refrences:
When should a class be Comparable and/or Comparator? http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html


My annotation lib for implementing Comparable and Comparator:

public class Person implements Comparable<Person> {         
    private String firstName;  
    private String lastName;         
    private int age;         
    private char gentle;         

    @Override         
    @CompaProperties({ @CompaProperty(property = "lastName"),              
        @CompaProperty(property = "age",  order = Order.DSC) })           
    public int compareTo(Person person) {                 
        return Compamatic.doComparasion(this, person);         
    }  
}

Click the link to see more examples. http://code.google.com/p/compamatic/wiki/CompamaticByExamples


Comparable - java.lang.Comparable: int compareTo(Object o1)

A comparable object is capable of comparing itself with another object. The class itself must implements the java.lang.Comparable interface in order to be able to compare its instances.

  • Capable of comparing current object with the provided object.
  • By using this we can implement only one sort sequence based on the instances properties. EX: Person.id
  • Some of the Predefined Classes like String, Wrapper classes, Date, Calendar has implemented Comparable interface.

Comparator - java.util.Comparator: int compare(Object o1, Object o2)

A comparator object is capable of comparing two different objects. The class is not comparing its instances, but some other class’s instances. This comparator class must implement the java.util.Comparator interface.

  • Capable of comparing any two Objects of Same Type.
  • By using this we can implement many sort sequence and name each, based on the instances properties. EX: Person.id, Person.name, Person.age
  • We can implement Comparator interface to our Pre-defined classes for Customized sorting.

Example:

public class Employee implements Comparable<Employee> {

    private int id;
    private String name;
    private int age;
    private long salary;

    // Many sort sequences can be created with different names.
    public static Comparator<Employee> NameComparator = new Comparator<Employee>() {         
        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };
    public static Comparator<Employee> idComparator = new Comparator<Employee>() {       
        @Override
        public int compare(Employee e1, Employee e2) {
            return Integer.valueOf(e1.getId()).compareTo(Integer.valueOf(e2.getId()));
        }
    };

    public Employee() { }
    public Employee(int id, String name, int age, long salary){
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    // setters and getters.

    // Only one sort sequence can be created with in the class.
    @Override
    public int compareTo(Employee e) {
    //return Integer.valueOf(this.id).compareTo(Integer.valueOf(e.id));
    //return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        if (this.id > e.id) {
            return 1;
        }else if(this.id < e.id){
            return -1;
        }else {
            return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        }

    }   

    public static void main(String[] args) {

        Employee e1 = new Employee(5, "Yash", 22, 1000);
        Employee e2 = new Employee(8, "Tharun", 24, 25000);

        List<Employee> list = new ArrayList<Employee>();
        list.add(e1);
        list.add(e2);
        Collections.sort(list); // call @compareTo(o1)
        Collections.sort(list, Employee.nameComparator); // call @compare (o1,o2)
        Collections.sort(list, Employee.idComparator); // call @compare (o1,o2)
    }
}
  • For customized sorting we go for comparator @compare(o1, o2) for other scenarios we go for comparable @compareTo(o1), with out changing code if we want to sort more than one field then we use comparator.

For Java 8 Lambda : Comparator refer to my post.