Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should a Java class implement comparable?

Tags:

java

People also ask

Why do we implement comparable in Java?

You need it to compare objects, in order to use, for example, sorting method of ArrayList class. You need a way to compare your objects to be able to sort them. So you need a custom compareTo() method in your class so you can use it with the ArrayList sort method. The compareTo() method returns -1,0,1.

What are the benefits of implementing the Comparable interface?

The benefit of implementing the interface is that some methods specifically require object that implements the Comparable interface. It gives them a guarantee that the object you're passing has a compareTo method with the correct signature.

Why do we use comparable?

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.

When a class implements to comparable interface what method must also be implemented?

For any class to support sorting, it should implement the Comparable interface and override it's compareTo() method. It returns a negative integer if an object is less than the specified object, returns zero if an object is equal, and returns a positive integer if an object is greater than the specified object.


Here is a real life sample. Note that String also implements Comparable.

class Author implements Comparable<Author>{
    String firstName;
    String lastName;

    @Override
    public int compareTo(Author other){
        // compareTo should return < 0 if this is supposed to be
        // less than other, > 0 if this is supposed to be greater than 
        // other and 0 if they are supposed to be equal
        int last = this.lastName.compareTo(other.lastName);
        return last == 0 ? this.firstName.compareTo(other.firstName) : last;
    }
}

later..

/**
 * List the authors. Sort them by name so it will look good.
 */
public List<Author> listAuthors(){
    List<Author> authors = readAuthorsFromFileOrSomething();
    Collections.sort(authors);
    return authors;
}

/**
 * List unique authors. Sort them by name so it will look good.
 */
public SortedSet<Author> listUniqueAuthors(){
    List<Author> authors = readAuthorsFromFileOrSomething();
    return new TreeSet<Author>(authors);
}

Comparable defines a natural ordering. What this means is that you're defining it when one object should be considered "less than" or "greater than".

Suppose you have a bunch of integers and you want to sort them. That's pretty easy, just put them in a sorted collection, right?

TreeSet<Integer> m = new TreeSet<Integer>(); 
m.add(1);
m.add(3);
m.add(2);
for (Integer i : m)
... // values will be sorted

But now suppose I have some custom object, where sorting makes sense to me, but is undefined. Let's say, I have data representing districts by zipcode with population density, and I want to sort them by density:

public class District {
  String zipcode; 
  Double populationDensity;
}

Now the easiest way to sort them is to define them with a natural ordering by implementing Comparable, which means there's a standard way these objects are defined to be ordered.:

public class District implements Comparable<District>{
  String zipcode; 
  Double populationDensity;
  public int compareTo(District other)
  {
    return populationDensity.compareTo(other.populationDensity);
  }
}

Note that you can do the equivalent thing by defining a comparator. The difference is that the comparator defines the ordering logic outside the object. Maybe in a separate process I need to order the same objects by zipcode - in that case the ordering isn't necessarily a property of the object, or differs from the objects natural ordering. You could use an external comparator to define a custom ordering on integers, for example by sorting them by their alphabetical value.

Basically the ordering logic has to exist somewhere. That can be -

  • in the object itself, if it's naturally comparable (extends Comparable -e.g. integers)

  • supplied in an external comparator, as in the example above.


Quoted from the javadoc;

This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method.

Lists (and arrays) of objects that implement this interface can be sorted automatically by Collections.sort (and Arrays.sort). Objects that implement this interface can be used as keys in a sorted map or as elements in a sorted set, without the need to specify a comparator.

Edit: ..and made the important bit bold.


The fact that a class implements Comparable means that you can take two objects from that class and compare them. Some classes, like certain collections (sort function in a collection) that keep objects in order rely on them being comparable (in order to sort you need to know which object is the "biggest" and so forth).


Most of the examples above show how to reuse an existing comparable object in the compareTo function. If you would like to implement your own compareTo when you want to compare two objects of the same class, say an AirlineTicket object that you would like to sort by price(less is ranked first), followed by number of stopover (again, less is ranked first), you would do the following:

class AirlineTicket implements Comparable<Cost>
{
    public double cost;
    public int stopovers;
    public AirlineTicket(double cost, int stopovers)
    {
        this.cost = cost; this.stopovers = stopovers ;
    }

    public int compareTo(Cost o)
    {
        if(this.cost != o.cost)
          return Double.compare(this.cost, o.cost); //sorting in ascending order. 
        if(this.stopovers != o.stopovers)
          return this.stopovers - o.stopovers; //again, ascending but swap the two if you want descending
        return 0;            
    }
}

An easy way to implement multiple field comparisons is with Guava's ComparisonChain - then you can say

   public int compareTo(Foo that) {
     return ComparisonChain.start()
         .compare(lastName, that.lastName)
         .compare(firstName, that.firstName)
         .compare(zipCode, that.zipCode)
         .result();
   }

instead of

  public int compareTo(Person other) {
    int cmp = lastName.compareTo(other.lastName);
    if (cmp != 0) {
      return cmp;
    }
    cmp = firstName.compareTo(other.firstName);
    if (cmp != 0) {
      return cmp;
    }
    return Integer.compare(zipCode, other.zipCode);
  }
}