Comparable vs Comparator in Java: What to Use?

comparable vs comparator in javaThere are 2 interfaces for sorting in Java: Comparator and Comparable.

I’ll explain to you how to define Comparator and Comparable objects, what’s the difference and when you can use it.

Let’s comparable vs comparator fight begin…

When to Use Comparable and Comparator

So what is the main difference between Comparator and Comparable interfaces?

Comparator vs Comparable what to use?

Just follow rules:

Use Comparable when:

  • If the class is under control and you can change its code.
  • If the comparable behavior should be default sorting behavior.

Use Comparator when:

  • You can’t change a code of the class
  • You need different sorting behaviors or you need to override default ordering.
  • Your sorting behavior should rely on external input parameters.
Tweet When to Use Comparable and ComparatorClick To Tweet

Natural Ordering in Java

For collections of Strings, it is natural to use alphabetical order.

For the collection of integer numbers the natural order is by numeric value—1 before 2, and so on…

Example:

Output:

This table contains all standard Java classes that implement Comparable interface and support natural ordering.

Class Natural Ordering
Byte Signed numerical
Character Unsigned numerical
Long Signed numerical
Integer Signed numerical
Short Signed numerical
Double Signed numerical
Float Signed numerical
BigInteger Signed numerical
BigDecimal Signed numerical
Boolean Boolean.FALSE < Boolean.TRUE
File System-dependent lexicographic on pathname
String Lexicographic
Date Chronological
CollationKey Locale-specific lexicographic

Enums support natural alphabetical ordering as well.

When you want to sort a collection of objects, array or use sorted collections, such as TreeSet, you have to define a sort order.

To define it you should use Comparable or Comparator interface.

Tweet about Natural Ordering in JavaClick To Tweet

Comparable interface

Let’s sort a list of Car objects.

The easiest way is defining sort rules in Car class itself.

We can extend Car class by implementing the Comparable interface.

The comparable interface defines one method:

It returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

Let’s sort our Car by its VIN field in alphabetical order:

*for meaningful output we override Car’s toString() method.

Output:

Look at the line (33).

We use compareTo() method from String class, comparing strings in alphabetical (lexicographic) order.

But what if we want to sort our Car by fuel left?

We can implement compareTo() in this way:

This method returns -1 if the compared car has more fuel, 1 if less and 0 if they are equal.

Output:

Actually, compareTo() could implement as sophisticated logic as we can do.

The only restriction is – it should satisfy the contract:

It returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

For all x and y:

  1. if x.compareTo(y) > 0 then y.compareTo(x) < 0, and if x.compareTo(y) < 0 then y.compareTo(x) > 0
  2. if x.compareTo(y) > 0 and y.compareTo(z) >0 then x.compareTo(z) > 0
  3. if x.compareTo(y) == 0 then for any z sign of x.compareTo(z) == sign of y.compareTo(z)
  4. if x.compareTo(y) throws exception y.compareTo(x) throws exception too

It throws NullPointerException if the specified object is null.

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

Reference:

Tweet about Comparable Interface in JavaClick To Tweet

Comparator interface

But what if we want to sort our Car by VIN first, and by fuel next?

For this case, Java provides mechanism how to define sorting behavior explicitly by providing an instance of the class, that implements interface Comparator.

It holds rules how to compare objects.

The interface looks like this:

It returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

Let’s create two new classes, that implement Comparator interface:

and

let’s check all example’s code:

Output:

We sorted our list of Cars object in two different ways: by VIN and by fuel.

Comparator.compare() has the same contract as Comparable.compareTo() method:

  1. if compare(x,y) > 0 then compare(y,x) < 0 and if compare(x,y) < 0 then compare(y,x) > 0
  2. if compare(x,y) > 0 and compare(y,z) then compare(x,z) > 0
  3. if compare(x,y) == 0 then for any z sign of x.compare(x,z) == sign  of compare(y,z)
  4. if compare(x,y) throws exception compare(y,x) throws exception too

In general case there is not required that if compare(x,y)==0 then x.equals(y), but usually it is.

It throws NullPointerException if an argument is null and this comparator does not permit null arguments.

It throws ClassCastException if the arguments’ types prevent them from being compared by this comparator.

As you can see, there is a difference with Comparable.compareTo(): method compare() permits null arguments.

Reference:

Tweet about Comparator Interface in JavaClick To Tweet

Java 8 Sorting Features

Java 1.8 provides a couple of additional useful features.

First, we got a reverse version of the comparator:

Output:

Another cool feature – we can chain comparators.

Suppose, we want to sort our cars by fuel, but for cars, that have an equal amount of fuel (it is an integer, so it is quite possible) we want black color car first.

For this case we can implement a small and easy Comparator:

and just add it to ComparatorCarsByFuel.

So we don’t need re-implement  ComparatorCarsByFuel, but we just chain it with a new one:

we have to change toString() method for Car object.

It indicates color now:

Output:

If you start to deep into Java 8 features, it is difficult to stop.

Tweet about Java 8 Sorting FeaturesClick To Tweet

Conclusion

So, The Collection Framework provides a full control on objects sorting behavior.

We could define ‘natural’ order for our custom objects by implementing the Comparable interface.

It’s a good solution for a case when we extend objects and its behavior is changed, so we could change the order they sorted without any changes in default sorting behavior.

Another important thing is encapsulation –  the behavior of object belongs to object.

In case, when we need to sort objects in any custom way, or we are using final classes, and have no chance to override default sort behavior of object we could use numerous instances of classes that implement Comparator interface.

Scroll Up