Sorting with Comparable and Comparator

Learn to sort a List of Objects by a field value. Note that if you have millions of records for sorting at a time then a database query is the best way. Otherwise, using either Comparable or Comparator interface is a very convenient approach.

1. Overview

In the examples given in this tutorial, we will be using the record type User. It has four fields: id, firstName, lastName and age. I have chosen these fields purposefully to show different usecases.

import java.io.Serializable;

public record User(Long id, String firstName, String lastName, Integer age) 
        implements Serializable {

    public User {
        if (age < 18) {
            throw new IllegalArgumentException("You cannot hire a minor person");
        }
    }
}

We will be using the given unsorted list and sorting it on different field values.

private static List<User> getUnsortedUsers() {
    return Arrays.asList(
            new User(1L, "A", "Q", Integer.valueOf(24)),
            new User(4L, "B", "P", Integer.valueOf(22)),
            new User(2L, "C", "O", Integer.valueOf(27)),
            new User(3L, "D", "N", Integer.valueOf(29)),
            new User(5L, "E", "M", Integer.valueOf(25)));
}

Moving on, we will be using the Comparable and Comparator interfaces for sorting on different field values.

2. Sorting with Comparable for Natural Ordering

2.1. Implementing Comparable Interface

Comparable interface provides a single method compareTo(T o) to implement by any class so that two objects of that class can be compared. This method is used for implementing the natural sorting behavior.

The User record after implementing the Comparable interface is as follows. The similar implementation can be done for class types as well. The default sorting has been done on the id field.

public record User(Long id, String firstName, String lastName, Integer age) 
        implements Serializable, Comparable<User> {

    public User {
        if (age < 18) {
            throw new IllegalArgumentException("You cannot hire a minor person");
        }
    }

    @Override
    public int compareTo(User o) {
        return this.id.intValue() - o.id.intValue();
    }
}

2.2. Collections.sort() Method

We can pass the List of objects in the sort() method that will sort the objects in their natural ordering i.e. by id field.

Collections.sort( list );

Check out the output in the console.

[User[id=1, firstName=A, lastName=Q, age=24], 
User[id=2, firstName=C, lastName=O, age=27], 
User[id=3, firstName=D, lastName=N, age=29], 
User[id=4, firstName=B, lastName=P, age=22], 
User[id=5, firstName=E, lastName=M, age=25]]

2.3. Stream.sorted() Method

Java Stream API has sorted() method that can sort a stream of items in the natural order. Note that stream operations do not modify the original collections, so the objects in the list will be unchanged.

List<User> sortedList = list.stream()
                          .sorted()
                          .collect(Collectors.toList());

3. Sorting with Comparator for Custom Ordering

3.1. Creating Comparator Instances

Let us assume that we want to sort the users list based on some other fields, for example, by firstName or age. We can modify the User record because it already implements the natural ordering by id field.

Here comes the Comparator interface to rescue. A Comparator can be used to define the custom ordering. To sort on different object fields, we can create multiple Comparator implementations.

For example, to sort the users list by firstName, we can create FirstNameSorter class that implements the Comparator.

import java.util.Comparator;

public class FirstNameSorter implements Comparator<User> {

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

Note that we can use the lambda expression for creating the inline Comparator instances, for single-time uses.

Comparator<User> firstNameSorter = (o1, o2) -> o1.firstName().compareTo(o2.firstName());

We can create group by sorting effect by combining multiple comparators using Comparator.thenComparing() method. For example, we can create a complex comparator fullNameSorter for sorting a list by first name and then by last name.

Comparator<User> firstNameSorter = (o1, o2) -> o1.firstName().compareTo(o2.firstName());
Comparator<User> lastNameSorter = (o1, o2) -> o1.lastName().compareTo(o2.lastName());

Comparator<User> fullNameSorter = firstNameSorter.thenComparing(lastNameSorter);

One more type of Comparator is worth discussing that is used for reverse ordering. We can get this reverse comparator by calling reversed() method on any comparator instance.

Comparator<User> reverseSorter = firstNameSorter.reversed();

Similar way, we can create as many comparators as needed in the applications.

3.2. Collections.sort()

To sort using Collection.sort() method, pass two method arguments. The first argument is the unsorted list and the second argument is the Comparator instance.

List<User> list = getUnsortedUsers();
Comparator<User> firstNameSorter 
	= (o1, o2) -> o1.firstName().compareTo(o2.firstName());

Collections.sort(list, firstNameSorter);

3.3. Stream.sorted()

To sort the stream items using comparator instance, we can pass the comparator as method argument to the sorted() method.

List<User> list = getUnsortedUsers();
Comparator<User> firstNameSorter 
	= (o1, o2) -> o1.firstName().compareTo(o2.firstName());

List<User> sortedList = list.stream()
                .sorted(firstNameSorter)
                .collect(Collectors.toList());

4. hashCode() and equals() Contract

If we have overridden equals() method in the User class, always remember to honor the contract between hashCode() and equals() methods.

If two objects are equal using equals() method then compareTo() method should return zero.

As a general practice, always use the same fields in both methods. If we are using id field in the equals() method then use the id field in compareTo() method also. An example implementation is given as follows:

import java.io.Serializable;
import java.util.Objects;

public record User(Long id, String firstName, String lastName, Integer age) 
        implements Serializable, Comparable<User> {

    public User {
        if (age < 18) {
            throw new IllegalArgumentException("You cannot hire a minor person");
        }
    }
    
    @Override
    public int compareTo(User o) {
        return this.id.intValue() - o.id.intValue();
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        return Objects.equals(id, other.id);
    }
}

5. Conclusion

In this Java Comparable and Comparator tutorial, we learned to implement both interfaces in different ways for different usecases. We also saw the use of both interfaces in Java Stream API.

Finally, we understood how to correctly override hashCode() and equals() method on objects to keep sorting functioning properly.

Happy Learning !!

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

33 thoughts on “Sorting with Comparable and Comparator”

  1. Hi,

    If we have generic list with different object, how can we sort it
    Below is the example…

    List list=new ArrayList();

    Laptop l1 =new Laptop(1,”Mac”,800)
    Laptop l2=new Laptop(2,”dell”,500)
    Laptop l3 =new Laptop(3,”acer”,200)
    list.add(l1);
    list.add(l2);
    list.add(l3);

    Book b1=new Book(1,”science”,500);
    Book b2=new Book(2″science”,600);
    Book b3=new Book(3,”science”,700);
    list.add(b1);
    list.add(b2);
    list.add(b3);

    I tried implementing compareTo() method in both the entity classes but it does work

    Both Laptop and Book has a common variable of price
    Can we sort this based on price? if so please help me doing it

    Thanks in advance

    Reply
    • I think you need to abstract out the price in a superclass and inherit the Laptop and Book from that. The superclass say Item should inherit from comparable and implement the interface compareTo() method based on the price. Hope this helps.

      public class MyItem implements Comparable<MyItem>  {
              double price;
      
      	@Override
      	public int compareTo(MyItem o) {
      		return Double.compare(this.getPrice(), o.getPrice());
      	}
      }
      List<MyItem> items = new ArrayList<MyItem>();
      ...
      ...
      Collections.sort(items );
      //This will give output 200, 500, 500, 600, 700, 800
      
      
      Reply
  2. Hi Lokesh,

    I have one Doubt that i have gone through java docs and it has mentioned that “Lists (and arrays) of objects that implement this interface can be sorted automatically by Collections.sort (and Arrays.sort). “.Then let me know is it true that sort() method internally calls compareTo() method or not.How it call internally

    Ex:

    public class Country implements Comparable{
    int countryId;
    String countryName;

    public Country(int countryId, String countryName) {
    super();
    this.countryId = countryId;
    this.countryName = countryName;
    }

    @Override
    public int compareTo(Country country) {
    return (this.countryId country.countryId ) ? 1:0 ;
    }

    public int getCountryId() {
    return countryId;
    }

    public void setCountryId(int countryId) {
    this.countryId = countryId;
    }

    public String getCountryName() {
    return countryName;
    }

    public void setCountryName(String countryName) {
    this.countryName = countryName;
    }

    }

    ComparableMain.java

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;

    public class ComparableMain {

    public static void main(String[] args) {
    Country indiaCountry=new Country(1, “India”);
    Country chinaCountry=new Country(4, “USA”);
    Country nepalCountry=new Country(3, “Russia”);
    Country bhutanCountry=new Country(2, “Japan”);

    List listOfCountries = new ArrayList();
    listOfCountries.add(indiaCountry);
    listOfCountries.add(usaCountry);
    listOfCountries.add(russiaCountry);
    listOfCountries.add(japanCountry);

    System.out.println(“Before Sort : “);
    for (int i = 0; i < listOfCountries.size(); i++) {
    Country country=(Country) listOfCountries.get(i);
    System.out.println("Country Id: "+country.getCountryId()+"||"+"Country name:"+country.getCountryName());
    }
    Collections.sort(listOfCountries);

    //Here Hoe it call compareTo() internally.

    Reply
  3. Hi lokesh,
    I am getting the following error when I want to override the method of Comparable interface.Can you please help me to know the reason for the error.The below is the source code which I have written.

    Error : The method compareTo(Employee) of type Employee must override a superclass method.

    public class Employee implements Comparable {
    private int empId;
    private String empName;
    private int age;
    private int salary;
    public int getEmpId() {
    return empId;
    }
    public void setEmpId(int empId) {
    this.empId = empId;
    }
    public String getEmpName() {
    return empName;
    }
    public void setEmpName(String empName) {
    this.empName = empName;
    }
    public int getAge() {
    return age;
    }
    public void setAge(int age) {
    this.age = age;
    }
    public int getSalary() {
    return salary;
    }
    public void setSalary(int salary) {
    this.salary = salary;
    }
    @Override
    public int compareTo(Employee e) {
    // TODO Auto-generated method stub
    return this.empId-e.empId;
    }
    @Override
    public String toString(){
    return “Employee Details: Emp Id: “+empId +” Name: “+empName+” Age: “+age+” Salary: “+salary;
    }

    }

    Thanks and Regards,
    Chandra.

    Reply
    • For null handling. refer to this guide.

      As per java docs of Comparable interface :

      “The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.”

      Reply
  4. Hi Lokesh,

    This is really a gud and lucid explanation but how can we write more null safe compare and compareto method. Entities with null reference or with null fields can slip to this method breaking the code.

    Regards,
    Himansu

    Reply
  5. Hi Lokesh,
    I know Comparable & Comparator interface, it is using for sorting objects. But i have some confusion in these 2 Interface when i use comparable & Comparator…?

    Reply
    • Use Comparable if you want to implement default sorting. e.g. Employees will be sort based on their employee ids. Comparator should be used when you are adding multiple ways of sorting or sorting on uncommon fields. e.g. sorting based on Lastname or emaployee age.

      Reply
      • Suppose I need to sort based on both fields Lastname AND employee age then? for example flight search Time and Price then how I would proceed?

        Reply
        • Before java 8 solution will be like this if you want to sort first by lastName and then firstName filed:

          import java.util.*;
          
          public class Name implements Comparable&amp;amp;lt;Name&amp;amp;gt; {
              private final String firstName, lastName;
          
             //More code
              public String toString() {
          	return firstName + &amp;amp;quot; &amp;amp;quot; + lastName;
              }
          
              public int compareTo(Name n) {
                  int lastCmp = lastName.compareTo(n.lastName); //First compare by last name
                  return (lastCmp != 0 ? lastCmp : firstName.compareTo(n.firstName)); //If lat name matches then compare by first name
              }
          }
          

          For java 8 solution, use something like this:

          List&amp;amp;lt;Employee&amp;amp;gt; employees  = getEmployees();
           
          //Sorting on multiple fields; Group by.
          Comparator&amp;amp;lt;Employee&amp;amp;gt; groupByComparator = Comparator.comparing(Employee::getFirstName)
                                                              .thenComparing(Employee::getLastName);
          employees.sort(groupByComparator);
          

          https://howtodoinjava.com/java8/comparator-example-lambda/

          Reply
  6. If you want to sort by any field of an object no need to write different comparators for each of the field, instead create the constructor for comparator and pass the sort field and sort direction and compare method to have code for each of the cases.

    Reply
  7. Please correct the following sentence in your blog 🙂

    Comparable interface provides one method compare(Object o) to implement in any class so that two instances of that class can be compared.

    from compare(Object o) to int compareTo(T o)

    May be that should be a typo 🙂

    Reply
  8. Is there any way to sort the objects with the Id’s in middle out fashion ,say objects with ids 1,2,3,4,5 are in order.I want to sort the order in the following manner 3 ,4,2,5,1

    Thank you.

    Reply
    • Hi Raj

      It’s really nice to heard your question.But in normal we are having two types of sorting i.e. Asc./Desc. But per your requirement You are looking something extra than these two.So if You find any solution Please share with us.

      Reply
  9. // Sorting user-defined object in TreeSet based on id, name
    import java.util.*;

    class Stud
    {
    int id;
    String name;

    Stud(int id, String name)
    {
    this.id=id;
    this.name=name;
    }

    public String toString()
    {
    return id + ” ” + name;
    }
    }

    class Studnames implements Comparator
    {
    public int compare(Stud s1, Stud s2)
    {

    return s1.name.compareTo(s2.name);
    }
    }

    class Studid implements Comparator
    {
    public int compare(Stud s1, Stud s2)
    {

    if(s1.id>s2.id)
    {
    return 0;
    }
    else if(s1.id>s2.id)
    {
    return -1;
    }
    else
    {
    return -1;
    }
    }
    }

    public class Al
    {

    public static void main(String args[])
    {

    SortedSet st = new TreeSet( new Studnames());
    st.add(new Stud(100,”amritsar”));
    st.add(new Stud(21,”bangalore”));
    st.add(new Stud(212,”orrisa”));
    st.add(new Stud(213,”mumbai”));

    System.out.println(“Sorting by names”);
    System.out.println(st);

    System.out.println(” “);

    System.out.println(“Sorting by id”);
    SortedSet st1 = new TreeSet( new Studid());
    st1.add(new Stud(101,”amritsar”));
    st1.add(new Stud(221,”bangalore”));
    st1.add(new Stud(124,”orrisa”));
    st1.add(new Stud(121,”mumbai”));

    System.out.println(st1);

    }

    }

    1. Have look at the above code
    2. Have used TreeSet to sort user defined objects based on id and names.
    3. OUTPUT:

    Sorting by names
    [100 amritsar, 21 bangalore, 213 o, 212 orrisa]

    Sorting by id
    [101 amritsar]

    4. why id’s not getting sorted only one id is getting displayed?

    Reply
    • Correct compare method for class Studid implements Comparator is:

      public int compare(Stud s1, Stud s2)
      {
      if (s1.id > s2.id) {
      return 1;
      } else if (s1.id < s2.id) { return -1; } else { return 0; } } Use above method and you will get correct output: Sorting by names [100 amritsar, 21 bangalore, 213 mumbai, 212 orrisa] Sorting by id [101 amritsar, 121 mumbai, 124 orrisa, 221 bangalore]

      Reply
      • Is there any way to sort the objects with the Id’s and Names ,say objects with ids 1,1,2,2 and Names with A,B,A,B.I want to sort the order in the following manner 1,1,A,A,2,2,B,B.

        Reply
        • If I will be in your place, then I will d this kind of sorting at database later while executing SELECT query. No need to put complex things in application, if desired result is easily achievable through one group by and one sort column in SELECT.

          Reply

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.