CodeGym /Courses /JAVA 25 SELF /Comparator Interface: Creation and Usage

Comparator Interface: Creation and Usage

JAVA 25 SELF
Level 29 , Lesson 3
Available

1. Introduction

In real life, one way to compare objects is rarely enough. Imagine you have a list of users: sometimes you want to sort them by first name, sometimes — by age, and sometimes — by the length of the last name. Or you have a class you didn’t author at all, and you can’t add compareTo to it. For such cases, Java has the Comparator interface.

When Comparable isn’t enough

  • You can’t modify the class (for example, it’s from a third-party library).
  • You need several ways to sort (by different fields).
  • You want to separate comparison logic from the class itself (for example, sort differently in different parts of the program).

Analogy
If Comparable is the built-in “natural order” of an object, then Comparator is an external judge that can evaluate your objects by any criteria: today by name, tomorrow — by age, and the day after — by name length.

2. The Comparator interface: syntax and contract

Interface declaration

public interface Comparator<T> {
    int compare(T o1, T o2);
}

The compare method should return:

  • A negative number if the first object is “less than” the second.
  • 0 if they are equal.
  • A positive number if the first is “greater than” the second.

The contract is the same as Comparable’s, except now you compare two objects directly rather than the “current” vs. “other” via compareTo.

Example: a comparator for sorting by last name

Suppose we have a Person class:

public class Person {
    private String firstName;
    private String lastName;
    private int age;

    // Constructor and getters
    public Person(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public int getAge() { return age; }
}

Create a comparator that will sort by last name:

import java.util.Comparator;

public class LastNameComparator implements Comparator<Person> {
    @Override
    public int compare(Person a, Person b) {
        return a.getLastName().compareTo(b.getLastName());
    }
}

Note: the compareTo method on strings (String) compares them in lexicographic order.

3. Using Comparator: sorting collections

Sorting with a comparator

import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Anna", "Kostetskaya", 25));
        people.add(new Person("Boris", "Novak", 20));
        people.add(new Person("Victoria", "Bell", 22));

        // Sort by last name
        Collections.sort(people, new LastNameComparator());

        for (Person p : people) {
            System.out.println(p.getLastName() + " " + p.getFirstName());
        }
    }
}

Result:

Novak Boris
Kostetskaya Anna
Bell Victoria

Sorting by age with a comparator

Even if the class already implements Comparable by name, you can create a separate comparator — by age:

public class AgeComparator implements Comparator<Person> {
    @Override
    public int compare(Person a, Person b) {
        return Integer.compare(a.getAge(), b.getAge());
    }
}

And use it similarly:

Collections.sort(people, new AgeComparator());

Result:

Boris Novak (20)
Victoria Bell (22)
Anna Kostetskaya (25)

Example: choosing a comparator on the fly

Collections.sort(people, new LastNameComparator()); // By last name
Collections.sort(people, new AgeComparator());      // By age

4. Anonymous classes and lambda expressions

You can create comparators “on the fly” without declaring separate classes.

Anonymous class

Collections.sort(people, new Comparator<Person>() {
    @Override
    public int compare(Person a, Person b) {
        return a.getFirstName().compareTo(b.getFirstName());
    }
});

Lambda expression

Collections.sort(people, (a, b) -> a.getFirstName().compareTo(b.getFirstName()));

Or even shorter with the list method List.sort:

people.sort((a, b) -> a.getFirstName().compareTo(b.getFirstName()));
  • Anonymous classes — the old way; verbose.
  • Lambdas — modern and compact.

5. Examples: sorting by different criteria

Sorting by last-name length

Comparator<Person> byLastNameLength = (a, b) ->
        Integer.compare(a.getLastName().length(), b.getLastName().length());
people.sort(byLastNameLength);

Sorting by age, then by first name (multi-level)

Comparator<Person> byAgeThenName = (a, b) -> {
    int cmp = Integer.compare(a.getAge(), b.getAge());
    if (cmp != 0) return cmp;
    return a.getFirstName().compareTo(b.getFirstName());
};
people.sort(byAgeThenName);

Using a comparator for searching (example)

A comparator is useful not only for sorting but also for searching in sorted collections:

// people must be sorted by age!
Person key = new Person("?", "?", 22);
int idx = Collections.binarySearch(people, key, new AgeComparator());
if (idx >= 0) {
    System.out.println("Found a person aged 22: " + people.get(idx));
}

6. Best practices and working with Comparator

Do not violate the contract

  • If compare(a, b) returns 0, then compare(b, a) must also return 0.
  • If compare(a, b) > 0, then compare(b, a) < 0.
  • Account for possible null values (see below).

Don’t forget about equals and hashCode

Although comparators compare objects “in their own way”, for structures like TreeSet or when looking up keys in TreeMap it’s important that the comparator’s comparison logic is consistent with equals. Otherwise, you may get unexpected results: two different objects are considered equal by the comparator but are not equal according to equals.

Sorting with nulls in mind

If fields can be null, use the built-in helpers:

Comparator<Person> byLastNameNullSafe = Comparator.comparing(
    Person::getLastName,
    Comparator.nullsLast(String::compareTo)
);
people.sort(byLastNameNullSafe);

7. Useful tips

Table: Comparable vs. Comparator

Comparable Comparator
Where is it implemented? In the class itself In a separate class/lambda
Method
int compareTo(T o)
int compare(T o1, T o2)
How many variants? Only one “natural” one As many as you need
Usage
Collections.sort(list)
Collections.sort(list, comp)
For third-party classes? No Yes

Example: sorting in descending order

You can invert the order manually:

Comparator<Person> byAgeDesc = (a, b) -> Integer.compare(b.getAge(), a.getAge());
people.sort(byAgeDesc);

Or using reversed():

Comparator<Person> byAge = Comparator.comparingInt(Person::getAge);
people.sort(byAge.reversed());

8. Common mistakes when working with Comparator

Error #1: Violating the comparison contract. If you forget that compare(a, b) and compare(b, a) must have opposite signs, or you return arbitrary values (for example, just the difference — a.getAge() - b.getAge(), which can overflow), the result will be unpredictable. Use Integer.compare rather than subtraction — it’s safer.

Error #2: Ignoring null values. If the fields you compare can be null, be sure to handle this case (for example, via Comparator.nullsFirst/Comparator.nullsLast), otherwise it’s easy to get a NullPointerException at the most unexpected moment.

Error #3: Unstable sorting criteria. If a comparator returns different values for the same objects (for example, it uses a random number or a highly mutable field), sorting may behave chaotically.

Error #4: Inconsistency with equals. If compare(a, b) == 0 but a.equals(b) is false, collections like TreeSet and TreeMap may not work as you expect. Ideally, equality by the comparator and by equals should match.

Error #5: Sorting without a comparator for third-party classes. If you try to sort objects of a “foreign” class without Comparable and without providing a Comparator, you’ll get a compilation error. Pass an explicit comparator.

1
Task
JAVA 25 SELF, level 29, lesson 3
Locked
Sorting the attendees list by age at an event 🧑‍🎓
Sorting the attendees list by age at an event 🧑‍🎓
1
Task
JAVA 25 SELF, level 29, lesson 3
Locked
Ordering participants by name length for badges 🏷️
Ordering participants by name length for badges 🏷️
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION