Java Comparable

Introduction

In this tutorial we will take an in-depth look at Java’s comparable interface, what it is, what it does and how to use it. We will also take a deep look at the general contract of Comparable and see the notorious ClassCastException and much elusive Consistency with equals, which are typical beginner mistakes.

Comparable

Comparable is an interface in java.lang package with a single method compareTo which is implemented by a class to ensure that its instances have a natural ordering.

What does it mean exactly?

Well, it means that the instances of the class implementing comparable can compare themselves and be arranged in a natural order. For example, the String class in java implements Comparable. So if you put strings in a TreeMap, the string instances will be sorted in alphabetical order aka lexicographical order.

Quick Demo

 

Signatures

So, if you are writing a class (more precisely a Value class) with an obvious natural ordering, such as alphabetical order, numerical order, or chronological order, you should implement Comparable and override compareTo method accordingly.

This is how the Comparable interface looks like:

 

A simple invocation of compareTo method would look like:

                                thisObject.compareTo(anotherObject);

The compareTo() method returns an int with the following characteristics:

  1.  negative Integer – If thisObject < anotherObject
  2.  zero – If thisObject == anotherObject
  3.  positive Integer – If thisObject > anotherObject

 

In the TreeMap example above, when you put a key-value pair in the TreeMap, the input key is inserted at its sorted place based on the compareTo implementation of String class, which enforces a natural ordering that is lexicographical. You must look at the TreeMap’s implementation of put() method.

Comparable Implementation

To further consolidate our understanding of Comparable, lets write our own comparable class and see how its objects compare with each other.

For this example we will write a Student class which has a natural ordering by Roll Number. Natural ordering by Name and Maximum marks obtained are also fine but we have already seen an example of alphabetic ordering in case of String class so I chose Roll Numbers. You can try Marks obtained for your compareTo implementation.

Custom Comparable Class

Main Class

 

General Contract of Comparable

Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

Throws ClassCastException if the specified object’s type prevents it from being compared to this object.

 

Reflexivity

  • The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y. (This implies that x.compareTo(y) must throw an exception iff y.compareTo(x) throws an exception.)

The first contract says that if you reverse the direction of a comparison between two object references, the expected thing happens: if the first object is less than the second, then the second must be greater than the first; if the first object is equal to the second, then the second must be equal to the first; and if the first object is greater than the second, then the second must be less than the first.

Transitivity

  • The implementor must also ensure that the relation is transitive: (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.

The second contract says that if one object is greater than a second, and the second is greater than a third, then the first must be greater than the third.

Symmetry

  • The implementor must ensure that x.compareTo(y) == 0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z.

Means that all objects that compare as equal must yield the same results when compared to any other object.

consistency with equals

  • It is strongly recommended, but not strictly required, that (x.compareTo(y) == 0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is “Note: This class has a natural ordering that is inconsistent with equals.”

This last paragraph of the compareTo contract, which is a strong suggestion rather than a contract, simply states that the equality test imposed by the compareTo method should generally return the same results as the equals method. If this provision is obeyed, the ordering imposed by the compareTo method is said to be consistent with equals. If it’s violated, the ordering is said to be inconsistent with equals. A class whose compareTo method imposes an order that is inconsistent with equals will still work, but sorted collections containing elements of the class may not obey the general contract of the appropriate collection interfaces (Collection, Set, or Map). This is because the general contracts for these interfaces are defined in terms of the equals method, but sorted collections use the equality test imposed by compareTo in place of equals. It is not a catastrophe if this happens, but it’s something to be aware of.

For example, consider the BigDecimal class, whose compareTo method is inconsistent with equals. If you create a HashSet instance and add new BigDecimal(“6.0”) and new BigDecimal(“6.00”), the set will contain two elements because the two BigDecimal instances added to the set are unequal when compared using the equals method. If, however, you perform the same procedure using a TreeSet instead of a HashSet, the set will contain only one element because the two BigDecimal instances are equal when compared using the compareTo method. (See the BigDecimal documentation for details.)

Related Posts

Recommended Reading