Sort list of objects

Suppose you have a list which contains objects of your own class rather than java’s built in classes such as java.lang.Integer, java.lang.String etc.

Now you want this list of objects to be sorted by field of this class.

Let’s say we have a User class which contains user details such as name, age, address etc., and we have a list of different users.
We want a list wherein users are listed in the order of their increasing age. That is, we need to sort the above list by a field named age.
In this article, we will understand how to do this using Collections.sort() method in java.


Collections.sort()
You can sort a list using sort() method of java.util.Collections class.

sort() is a static method and can be invoked as Collections.sort().

sort() accepts a list of objects to be sorted.

If the list is of built in java types such as String, Integer etc., sort() will sort it in ascending order by default.

But if you pass a list of custom objects to this method, how does this method know the criteria to sort the list or the field of objects by which it should sort.
That is, whether it should sort on name, age or some other field.

Thus, we need some way to inform sort() method about the field on which it should sort the list.

There are two overloaded versions of sort() method in Collections. Their signatures are

1. sort(List<T> list)
It takes a list that needs to be sorted as argument.

2. sort(List<T> list, Comparator<? super T>c)
It takes the list to be sorted and an object that implements Comparator interface as arguments.

Both these methods will be discussed in this article
Method 1: sort(List<T> list)
This method accepts a list as argument. Note that the list is generic. This is because it can contain objects of any type.

Objects of the list must implement Comparable interface.

java.util.Comparable interface defines the method of ordering  objects of a class.

It defines a way in which the objects should be compared while sorting.
This interface has a compareTo() method which compares two objects based on single or multiple fields.

This method takes an object as argument and returns a value

  • <0   if the object on which compareTo() is called is lesser than the supplied argument,
  •   0    if both the objects are equal, and
  • >0   if the object on which compareTo() is called is greater than the supplied object.

If you want to sort a list of objects of a class, then the class should implement java.util.Comparable interface and its compareTo() method.
This method will define on what basis(or on which fields), the objects should be sorted.

Thus, if you want to sort a list of you own User class, then define this class as shown below.

import java.util.Comparable;

//The User class
public class User implements Comparable {
  // age of user. We need to sort on this
  int age;

  // constructor
  public User(int age) {
    this.age = age;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  /* Since the class implements Comparable, 
   * we need to provide compareTo() 
   */ implementation
  @Override
  public int compareTo(User o) {
    // return the difference between ages of two users
    return this.age - o.getAge();
  }

}

As you can see, compareTo() method compares objects on the basis of age.
Below is the code which creates different User objects, add them to a list and then sort it.

Note that the program first prints the unsorted list and then the sorted list.

import java.util.Collections;

//main class
public class Sorter {
   public static void main(String[] args) {
     // create a list
     List list = new ArrayList();
     // create users
     User user1 = new User(10);
     User user2 = new User(20);
     User user3 = new User(92);
     User user4 = new User(1);
     // add users to list
     list.add(user1);
     list.add(user2);
     list.add(user3);
     list.add(user4);
     System.out.println("Before Sorting...");
     // iterate over unsorted list
     for (User u : list) {
       // this will print 10,20,92,1. 
      System.out.println("User age: " + u.getAge());
     }
     // now sort the list
     Collections.sort(list);
     System.out.println("After Sorting...");
     // iterate over sorted list
     for (User u : list) {
       // this prints 1,10,20,92
       System.out.println("User age: " + u.getAge());
     }
  }
}

Above code prints

Before Sorting…
User age: 10
User age: 20
User age: 92
User age: 1
After Sorting…
User age: 1
User age: 10
User age: 20
User age: 92

See! our list is sorted on the basis of age.
Similarly, if you want to sort the list of objects on some other field, then modify the compareTo() method.
Above example uses an enhanced for loop to iterate over the list of objects but you can use other methods to iterate over the list.

This method can also be used to sort a list of strings in reverse order alphabetically.


Method 2: Sort with custom comparator
There is an overloaded version of Collections.sort(), which accepts two arguments:
1. List to be sorted,
2. An object that implements Comparator interface.

and sorts the list as per the logic defined in comparator object.

java.util.Comparator interface has a compare() method which takes two objects as arguments and returns an integer whose value can be

  • <0   if the first object is lesser than the second,
  •   0    if both the objects are equal, and
  • >0   if the first object is greater than the second.

This method is useful when you cannot modify the class whose objects need to be sorted, you could then supply an external comparator to sort().

Example that sorts a list of objects with this method is given below.

import java.util.Collections;
import java.util.Comparator;

// compartor class
class AgeComparator implement Comparator<User> {
  
   @Override
   public int compare(User u1, User u2) {
      // compare ages
      return u1.getAge() - u2.getAge();
   }
}

//main class
public class Sorter {
   public static void main(String[] args) {
     // create a list
     List list = new ArrayList();
     // create users
     User user1 = new User(10);
     User user2 = new User(20);
     User user3 = new User(92);
     User user4 = new User(1);
     // add users to list
     list.add(user1);
     list.add(user2);
     list.add(user3);
     list.add(user4);
     System.out.println("Before Sorting...");
     // iterate over unsorted list
     for (User u : list) {
       // this will print 10,20,92,1. 
      System.out.println("User age: " + u.getAge());
     }

     // create an object of comparator
     AgeComparator c = new AgeComparator();
     // now sort the list using comparator
     Collections.sort(list, c);
     System.out.println("After Sorting...");
     // iterate over sorted list
     for (User u : list) {
       // this prints 1,10,20,92
       System.out.println("User age: " + u.getAge());
     }
  }
}

Note the class that implements Comparator overrides its compare() method and returns an integer which is a difference between age of User objects.

Output of this program is

Before Sorting…
User age: 10
User age: 20
User age: 92
User age: 1
After Sorting…
User age: 1
User age: 10
User age: 20
User age: 92

Instead of creating a separate class and object for Comparator, implementation of compare can be supplied inline to sort method as an anonymous inner class as shown below.

Collections.sort(list, new Comparator<User>() {

   @Override
   public int compare(User u1, User u2) {
     return u1.getAge() - u2.getAge();
   }
});

Sort list objects java 8
Starting java 8, the implementation of compare method supplied to sort can be further be shortened by writing it as a Lambda expression as shown below.

Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());

Type of arguments of a Lambda expression are inferred by the compiler according to the context.

Let’s tweak in

  1. In order to sort the list using sort method of java.util.Collections class, the elements of the list MUST implement java.lang.Comparable interface.
    This is because Collections.sort() internally casts the objects to java.lang.Comparable.
    If the object has not implemented Comparable, a compiler error will be generated.
  2. If you declare a class to implement java.lang.Comparable and you do not provide implementation or override equals() method then a compiler error will be raised.
  3. The signature of the compareTo() method in the class implementing java.lang.Comparable should exactly match the signature of compareTo() method in Comparable interface.
    That is, public int compareTo(T o);
    If you change the return type of the method implementation in your class, you will face a compiler error The return type is incompatible with Comparable.compareTo(User)
  4. Collections.sort() method internally converts list to an array and this array is then iterated.
  5. During iteration, compareTo() method is invoked on all elements of array one by one.
    One object calls it and object at the next location is passed as an argument to this method as:
    (array[j-1]).compareTo(array[j])

    This is when the
    compareTo method defined in our class gets called.
  6. Object equality is calculated based on the criteria or the field chosen in the compareTo() method implementation.
    In our case, it was the age but it can be anything such as alphabetical order of user name.
  7. List objects can be sorted on multiple fields and this should be defined in compareTo() or compare() methods.

Hope you clearly understood how to sort a list of objects by field using Collections.sort() method.

Leave a Reply

Your email address will not be published. Required fields are marked *