Comparable vs Comparator in Java

Upasana | August 14, 2020 | 3 min read | 350 views


Both comprabale and comparator are interfaces from Java Collections Framework that allow sorting of collections. But both of these interfaces are meant for different purpose.
Comparable Comparator

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

It is a comparison function, which imposes a total ordering on some collection of objects.

Comparable provides only single sorting sequence (based on single or multiple fields)

N number of comparators can be created for different sorting sequences

Comparable affects the original class

Unlike Comparable, Comparator is external to original class that we are comparing.

Comparable provides compareTo(T other) method for defining natural ordering of class

Comparator provides compare(T o1, T o2) to sort elements

It is part of java.lang package

It is part of java.util package

Collection.sort(List), SortedMap and SortedSet can use this

This function can be passed to Collections.sort(List, Comparator), SortedSet and SortedMap to allow precise control of sorting order.

It is strongly recommended that natural ordering be consistent with equals, else sorted set and sorted map will behave strangely. Virtually all Java classes that implement comparable have natural orderings that are consistent with equals

No such requirements

Example Code

Lets take a concrete example of Comparable and Comparator

Person class with Natural ordering based on name
class Person implements Comparable<Person>{
    int age;
    String name;
    String email;

    @Override
    public int compareTo(Person o) {
        return name.compareTo(o.getName());
    }
}

Now we can sort a collection of persons based on its natural ordering using Collections.sort method.

Sorting in Natural Order
List<Person> students = Arrays.asList(
        new Person(20,"Bob", "bob@mail.com"),
        new Person(19, "Jane", "Jane@mail.com"),
        new Person(21,"Foo", "foo@mail.com")
);
Collections.sort(students);

This program will always sort persons based on their name i.e. Bob → Foo and thenJane.

Lets define two comparators now, one for name based comparison and another on basis of age.

class NameComparator implements Comparator<Person> {

    @Override
    public int compare(Person o1, Person o2) {
        return o1.name.compareTo(o2.name);
    }
}

class AgeComparator implements Comparator<Person> {

    @Override
    public int compare(Person o1, Person o2) {
        return ((Integer)o1.age).compareTo(o2.age);
    }
}

Now we can specify one of the above defined sorting function to choose sorting on demand:

Soritng based on Age
List<Person> students = Arrays.asList(
        new Person(20,"Bob"),
        new Person(19, "Jane"),
        new Person(21,"Foo")
);
Collections.sort(students, new AgeComparator());

Eay enough?

Chaining of comparison operations in Java 8

Java 8’s lambda expressions brings syntax improvements to sorting capabilities of Java Collections Framework. We can even chain multiple sorting operations using chaining of operations, as shown in below code:

Comparing by name and then age (if name is same)
List<Person> students = Arrays.asList(
        new Person(20,"Foo"),
        new Person(19, "Jane"),
        new Person(21,"Foo")
);
Collections.sort(students);
students.stream()
        .sorted(Comparator
                .comparing(Person::getName)
                .thenComparing(Person::getAge)
        ).forEach(System.out::println);

Even reverse sort is easy enough:

Soritng by Name and then Age in reverse order
students.stream()
        .sorted(Comparator
                .comparing(Person::getName)
                .thenComparing(Person::getAge)
                .reversed() (1)
        ).forEach(System.out::println);
1 Sorting Age in reverse direction. reversed() Returns a comparator that imposes the reverse ordering of this comparator.

Case insensitive ordering

For certain fields like email, case does not matter. Its easy to achieve this using Java 8:

Case insensitive ordering for email
students.stream()
        .sorted(Comparator
                .comparing(Person::getName)
                .thenComparing(Person::getAge)
                .thenComparing(Person::getEmail, Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))
        ).forEach(System.out::println); (1)
1 null values are pushed to last

Top articles in this category:
  1. Fail-Safe vs Fail-Fast Iterator in Java Collections Framework
  2. Http download using Java NIO FileChannel
  3. Java 8 Parallel Stream custom ThreadPool
  4. How will you increment each element of an Integer array, using parallel operation
  5. What are four principles of OOP, How aggregation is different than Composition?
  6. Troubleshooting Deadlock in Java
  7. What is AtomicInteger class and how it works internally

Recommended books for interview preparation:

Find more on this topic: