How to create this java comparator

Learn how to create this java comparator with practical examples, diagrams, and best practices. Covers java, comparator development techniques with visual explanations.

Mastering Java Comparators: Custom Sorting for Any Object

Hero image for How to create this java comparator

Learn how to implement Java's Comparator interface to define custom sorting logic for collections of objects, enabling flexible and powerful data manipulation.

In Java, sorting collections of objects is a common task. While many classes provide natural ordering (e.g., String, wrapper classes for primitives), you often need to sort objects based on custom criteria. This is where the java.util.Comparator interface comes into play. A Comparator allows you to define an external comparison logic, separate from the object's class, providing immense flexibility.

Understanding the Comparator Interface

The Comparator interface defines a single abstract method: compare(T o1, T o2). This method takes two objects of the same type T and returns an integer value indicating their relative order:

  • Negative integer: If o1 should come before o2.
  • Zero: If o1 and o2 are considered equal for sorting purposes.
  • Positive integer: If o1 should come after o2.

This simple contract allows you to implement virtually any sorting logic. Unlike the Comparable interface, which defines a natural ordering within the class itself, Comparator provides external ordering. This means you can have multiple Comparator implementations for the same class, each sorting by a different attribute or in a different order (e.g., ascending by name, descending by age).

classDiagram
    interface Comparator<T> {
        +int compare(T o1, T o2)
        +boolean equals(Object obj)
        +Comparator<T> reversed()
        +Comparator<T> thenComparing(Comparator<? super T> other)
        +static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor)
    }
    class MyObject {
        -String name
        -int age
        +MyObject(String name, int age)
        +String getName()
        +int getAge()
    }
    class MyObjectByNameComparator {
        +int compare(MyObject o1, MyObject o2)
    }
    class MyObjectByAgeComparator {
        +int compare(MyObject o1, MyObject o2)
    }

    Comparator <|-- MyObjectByNameComparator
    Comparator <|-- MyObjectByAgeComparator
    MyObjectByNameComparator ..> MyObject
    MyObjectByAgeComparator ..> MyObject

Class diagram illustrating the Comparator interface and its implementations for a custom MyObject.

Implementing a Custom Comparator

Let's consider a simple Person class with name and age fields. We want to sort a list of Person objects first by name, and then by age if names are identical. We'll create a Person class and then a PersonComparator.

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

The Person class, which we will sort using a custom Comparator.

import java.util.Comparator;

public class PersonComparator implements Comparator<Person> {

    @Override
    public int compare(Person p1, Person p2) {
        // First, compare by name
        int nameComparison = p1.getName().compareTo(p2.getName());

        // If names are different, return the name comparison result
        if (nameComparison != 0) {
            return nameComparison;
        } else {
            // If names are the same, compare by age
            return Integer.compare(p1.getAge(), p2.getAge());
        }
    }
}

A custom PersonComparator that sorts Person objects by name, then by age.

Using the Comparator

Once you have a Comparator implementation, you can use it with various sorting methods provided by the Java Collections Framework, such as Collections.sort() or List.sort().

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

public class ComparatorExample {
    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Alice", 28));
        people.add(new Person("Charlie", 35));
        people.add(new Person("Bob", 30));

        System.out.println("Original list:\n" + people);

        // Sort using the custom Comparator
        Collections.sort(people, new PersonComparator());

        System.out.println("\nSorted by name then age:\n" + people);

        // Example of sorting by age in descending order using a lambda
        people.sort((p1, p2) -> Integer.compare(p2.getAge(), p1.getAge()));
        System.out.println("\nSorted by age (descending) using lambda:\n" + people);
    }
}

Demonstrates how to use the PersonComparator to sort a list of Person objects.

The output of the above example would be:

Original list:
[Person{name='Alice', age=30}, Person{name='Bob', age=25}, Person{name='Alice', age=28}, Person{name='Charlie', age=35}, Person{name='Bob', age=30}]

Sorted by name then age:
[Person{name='Alice', age=28}, Person{name='Alice', age=30}, Person{name='Bob', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=35}]

Sorted by age (descending) using lambda:
[Person{name='Charlie', age=35}, Person{name='Bob', age=30}, Person{name='Alice', age=30}, Person{name='Alice', age=28}, Person{name='Bob', age=25}]

Leveraging Java 8+ Comparator Features

Java 8 introduced significant enhancements to the Comparator interface, making it much easier to create and combine comparators using lambda expressions and default methods. Key methods include:

  • Comparator.comparing(Function keyExtractor): Creates a Comparator that extracts a sort key from an object and sorts by that key.
  • thenComparing(Comparator other): Used to chain comparators, providing secondary, tertiary, etc., sort criteria.
  • reversed(): Returns a comparator that imposes the reverse ordering of this comparator.

These features allow for highly concise and readable comparator definitions.

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class ModernComparatorExample {
    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Alice", 28));
        people.add(new Person("Charlie", 35));
        people.add(new Person("Bob", 30));

        System.out.println("Original list:\n" + people);

        // Sort by name, then by age using Java 8+ features
        Comparator<Person> nameThenAgeComparator = Comparator.comparing(Person::getName)
                                                          .thenComparing(Person::getAge);
        people.sort(nameThenAgeComparator);
        System.out.println("\nSorted by name then age (Java 8+):\n" + people);

        // Sort by age in descending order
        Comparator<Person> ageDescendingComparator = Comparator.comparing(Person::getAge).reversed();
        people.sort(ageDescendingComparator);
        System.out.println("\nSorted by age descending (Java 8+):\n" + people);
    }
}

Using Java 8+ Comparator features for concise sorting logic.

1. Define Your Object Class

Create the class for the objects you want to sort, ensuring it has the necessary getter methods for the fields you'll use in your comparison logic.

2. Implement the Comparator Interface

Create a new class that implements java.util.Comparator<YourObject> and override the compare(YourObject o1, YourObject o2) method. Alternatively, use lambda expressions for simpler comparators.

3. Define Comparison Logic

Inside the compare method, implement the logic to determine the relative order of o1 and o2. Return a negative integer, zero, or a positive integer as per the Comparator contract. For multiple criteria, chain comparisons.

4. Use the Comparator for Sorting

Pass an instance of your Comparator to sorting methods like Collections.sort(List<T> list, Comparator<? super T> c) or List.sort(Comparator<? super E> c).