Sorting Collection of Objects by Multiple Fields in Java

Examples of sorting a Java Collection of Objects based on multiple fields.

Overview

In Java, custom Objects consist of fields. While working with a Collection of custom objects, we need to handle the equals() and the hashCode() methods, as they may depend on specific fields of the Objects. Similarly, we often need to sort a Collection of custom Java Objects based on the fields.

Sometimes the sorting logic becomes complicated, especially when we want to sort a Collection based on multiple fields of Objects. This tutorial demonstrates several strategies we can implement to sort a Collection of Objects using multiple fields.

Sample Collection of Java Objects

Example Class with Fields

Let’s create a class Student with a couple of fields.

@RequiredArgsConstructor
class Student {
  private final long id;
  private final String name;
  private final int age;
}Code language: PHP (php)

We will compare two Student instances based on their name and age.

Example Collection of Objects

Let’s create a Java Collection containing a few instances of our Student class.

List<Student> collection = List.of(
    new Student(1L, "Ray", 18),
    new Student(2L, "Bee", 18),
    new Student(3L, "Ray", 17),
    new Student(4L, "Bia", 15),
    new Student(5L, "Ria", 19)
);Code language: PHP (php)

We will sort this Collection of custom objects by comparing the multiple fields of the class.

Multi-Field Sort using Custom Comparator

The compare() method of the Comparator interface is a function that compares two Java Objects of the same type. We can pass the Comparator function to Collection sorting methods like the Collections.sort() or Arrays.sort() to customize and control the sorting order of the elements.

Let’s understand how we can use a custom Comparator function to sort Collections based on multiple fields.

Field-by-Field Comparison

Let’s create a simple Comparator and compare the fields one after the other.

Let’s create a simple Comparator to sort a Collection of objects using multiple fields.

public class SimpleComparator 
    implements Comparator<Student> {
  @Override
  public int compare(Student o1, Student o2) {
    int difference = o1.getName().compareTo(o2.getName());

    return (difference != 0) ? difference
        : Integer.compare(o1.getAge(), o2.getAge());
    }
  }
}Code language: Java (java)

The compare() method first compares the Student names, and if the Student names are the same, it compares their ages. Please note that it uses String’s compareTo() method and an Integer’s compare() method to compare the String and int values, respectively.

Let’s use the Comparator instance with the sort() method of the Java List and examine the outcome.

collection.sort(new StudentComparator());
collection.forEach(System.out::println);

//prints:
//Student(id=2, name=Bee, age=18)
//Student(id=4, name=Bia, age=15)
//Student(id=3, name=Ray, age=17)
//Student(id=1, name=Ray, age=18)
//Student(id=5, name=Ria, age=15)Code language: Java (java)

Our custom Comparator function that compares multiple fields one by one works. However, this approach is more error-prone as it is not uncommon to miss an edge case, especially when comparing using several fields.

Guava’s ComparisonChain

Google’s Guava library provides ComparisonChain, an abstraction to avoid the complexity of comparing fields manually. Let’s add the Guava dependency to our project.

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>31.1-jre</version>
</dependency>Code language: HTML, XML (xml)

We can now use the ComparisonChain abstraction to write the multi-field comparison more cleanly.

public class GuavaBasedComparator 
    implements Comparator<Student> {
  @Override
  public int compare(Student o1, Student o2) {
    return ComparisonChain.start()
        .compare(o1.getName(), o2.getName())
        .compare(o1.getAge(), o2.getAge())
        .result();
  }
}Code language: Java (java)

The Guava’s ComparisonChain simplifies the logic and makes the code more readable and less error-prone irrespective of the number of fields under comparison. We can now focus on comparing the right fields in the right order.

Apache Commons’s CompareToBuilder

The Apache Commons library provides a similar abstraction to help sort a Collection of Objects using multiple fields. Let’s add the right dependency to our project.

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.12.0</version>
</dependency>Code language: HTML, XML (xml)

Let’s implement our Comparator using the Apache Commons’ CompareToBuilder abstraction.

public class ApacheCommonsBasedComparator 
    implements Comparator<Student> {
  @Override
  public int compare(Student o1, Student o2) {
    return new CompareToBuilder()
        .append(o1.getName(), o2.getName())
        .append(o1.getAge(), o2.getAge())
        .build();
  }
}Code language: Java (java)

Like Guava, Apache Common’s CompareToBuilder abstraction hides the complexity of comparing different fields and handling the edge cases.

Multi-Field Sort using Comparator Factory – comparing()

Alternatively, the Comparator class provides a factory method to build a Comparator function using Lambda Expressions. Since the Java 8 release, we can add static methods in interfaces. The Comparator interface’s static comparing() method accepts a function that returns a Comparator that contains the comparison logic to compare the elements.

Instead of a lambda expression, we use a method reference to the getName() method.

collection.sort(Comparator
    .comparing(Student::getName)
    .thenComparing(Student::getAge));Code language: Java (java)

The Comparator interface’s static thenComparing() method creates a compound Comparator instance to help us sort a Collection based on multiple fields.

The comapring() and thenComparing() methods hide all the complexities behind them and provide a concise and readable syntax.

Summary

We examined several ways to sort a Collection of Java Custom Objects using multiple fields. To sort a Collection of Objects, we need a Comparator that can compare two instances of the same type under comparison. The Comparator instances are created either by overriding the compareTo() method or by using the comparing(), a static factory method.

Refer to our GitHub Repository for the complete source code of the examples.

Related Posts: