Learnerslesson
   JAVA   
  SPRING  
  SPRINGBOOT  
 HIBERNATE 
  HADOOP  
   HIVE   
   ALGORITHMS   
   PYTHON   
   GO   
   KOTLIN   
   C#   
   RUBY   
   C++   




Java - Comparable

Comparable makes a class eligible to be compared with itself by implementing it.

Don't be scared by the definition, we will clear it with below example.

We have seen Collections.sort(..). But in some cases it doesn't work. Let us see below :


The Problem with 'Collections.sort(..)'

Let us see how 'Collections.sort(..)' sorted the Strings in the above case :

1. We have added three Strings to an ArrayList.

2. Passed the ArrayList to 'Collections.sort(..)'


Collections.sort(aListString);


3. Internally 'Collections.sort(..)' opened the ArrayList, found three Strings in it and sorted them accordingly.

Now, consider the Human object. Human object might contain a variable named 'age', also it can contain a variable called 'name'.


class Human{
   int age;
   String name;
}

So, if we repeat the same steps with Human object.

i.e. We create three 'Human' objects and add it to an ArrayList. Then pass the ArrayList to 'Collections.sort(..)'.

'Collections.sort(..)' opens the ArrayList and is confused because it has three 'Human' objects. And 'Collections.sort(..)' doesn't know either to sort on 'age' or 'name'.

And Comparable comes to rescue.



Using Comparable for Sorting

Let's take the example of Human class.


class Human{

int age;
String name;

-- getter, setter & ctors ----

}

Let us create four 'Human' objects using constructor and add them to a List.


public class TestCollection{
  public static void main(String[] arg){

  Human human1 = new Human(34,"Tom");
  Human human2 = new Human(21,"Harry");
  Human human3 = new Human(27,"Mona");
  Human human4 = new Human(43,"Lily");
  List <Human> listHuman = new ArrayList <Human>();
  listHuman.add(human1);
  listHuman.add(human2);
  listHuman.add(human3);
  listHuman.add(human4); // All the Humans are added to the List.

  Collections.sort(listHuman);
  }
}

Sorting the 'Human' object by 'age'

Since, 'Collections.sort(..)' contains a List of Human objects :


Collections.sort(listHuman);

And we need to sort by 'age' and not by name. So, we have to make some tweak in the code telling 'Collections.sort(..)' to sort by 'age'.

And so comes Comparable for rescue. For solving this problem the Human Class needs to implement the Comparable interface and override the compareTo() method.

Tough to understand? Let's make it easy.


class Human implements Comparable <Human>{

  int age;
  String name;

  public Human(int age, String name){
    this.age = age;
    this.name = name;
  }

  -- getters & setters --

  public int compareTo(Human humanCompare){

    return (age - humanCompare.getAge());
  }

}

We need to follow the below two rules for using 'Comparable' :

1. Implement the 'Comparable' interface.


class Human implements Comparable <Human>

We have used Generics with Comparable (i.e. Comparable <Human> ), so that the sorting or comparison happens only with the 'Human' class.

2. Override the 'int compareTo(Object o)' in Human class.


public int compareTo(Human humanCompare){

   return (age - humanCompare.getAge());
}

*  The Comparable interface has an 'int compareTo(Object o)' method, which the Human class needs to override. And the fun is, compareTo(..) can accept 'Human humanCompare' as parameter instead of 'Object o'.


public int compareTo(Human humanCompare)

It is only because of Generics we have added with Comparable :


class Human implements Comparable <Human>

*  And the statement :


return (age - humanCompare.getAge());

is sorting the 'Human' objects in the list 'listHuman' in ascending based on 'age'.

Let us relook the process of sorting a List of Human objects by Collection.sort() :

1. We have added three Human objects to an ArrayList.

2. Passed the ArrayList to 'Collections.sort(..)'


Collections.sort(listHuman);

3. Internally 'Collections.sort(..)' opened the ArrayList, found three Human objects in it.

4. Since, Human is a class, 'Collections.sort(..)' searches if Human class implements Comparable and overrides 'int compareTo(Object o)'.

5. Now, Collections.sort() expects is a '0', '+ve' or '-ve' returned from compareTo(). Where,


'0' which means equal so do not sort.
'-ve' means sort in ascending order.
'+ve' means sort in descending order.

So, in 'return (age - humanCompare.getAge())' the List is sorted in ascending order. And if we would have done the opposite i.e 'return (humanCompare.getAge() - age)' the List would be sorted in descending order.


Sorting the 'Human' object by 'name'

Now, let us sort the Humans by 'name'.

Similarly, we have to change the compareTo() method of Human class.


class Human implements Comparable <Human>{

  int age;
  String name;

  public Human(int age, String name){
    this.age = age;
    this.name = name;
  }

  -- getters & setters --

  public int compareTo(Human humanCompare){

    return (name.compareTo(humanCompare.getName()));
  }
}

But for sorting the Strings we are not substracting the values. Instead we are calling compareTo() again.


return (name.compareTo(humanCompare.getName()));

Don't panic, think for a while. Can we substract two Strings? No right.

So, we followed the above rule. Look closely, 'name' is a String and we are calling compareTo() on the String.

So, does that mean the String class has a compareTo() method?

Yes, it has. And thats because the String class implements the Comparable interface and overrides the compareTo() method.


How does Collections.sort() sort a String?

Now, does it ring a bell. As in when we were sorting String objects, how it automatically got sorted?

That was because the String class had already implemented the Comparable interface and overriden the compareTo(). Same way we did it for the Human class.


Note : All the wrapper classes implement the Comparable interface and overrides the compareTo() method.

Natural Ordering

If we want to sort a list which only consist of a variable of a wrapper class (i.e. Integer, String, e.t.c). We do not have to write extra codes to sort them using 'Collections.sort()'. That is because all wrapper classes implement the Comparable interface and overrides the compareTo() method. This ordering is termed as natural ordering.


The Problem with Comparable

There were problems with Comparable which was a common problem for everyone.

1. We do not want to put compareTo() method in the Human class. Because Human class is a bean and it is never a good practice to put business logic in a bean.

2. Say at times we want to sort by 'name' and sometimes we want to sort by 'age'. This is not possible using Comparable. With Comparable, you can either sort by 'name' or by 'age'.

Due to the above restrictions, here comes a new interface with better resolution, which is Comparator.