Comparator Interface

About

The Comparator<T> @FunctionalInterface interface in Java is part of the java.util package and is used to define custom sorting logic for objects. Unlike Comparable<T>, which imposes a natural ordering within a class, Comparator<T> allows sorting logic to be defined externally.

Key Features of Comparator

  • Enables custom sorting for objects.

  • Can be used to sort objects based on multiple fields.

  • Provides various default methods (since Java 8) for enhanced functionality.

  • Supports method references and lambda expressions for concise syntax.

Abstract Methods

int compare(T o1, T o2)

This is the only abstract method in Comparator. It must be implemented to define custom comparison logic.

Compares two objects and returns: - Negative if o1 < o2 - Zero if o1 == o2 - Positive if o1 > o2

Comparator<Integer> intComparator = (a, b) -> Integer.compare(a, b);
System.out.println(intComparator.compare(10, 20)); // Output: -1
import java.util.*;

public class ComparatorExample {
    public static void main(String[] args) {
        Comparator<Integer> intComparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2; // Ascending order comparison
            }
        };

        List<Integer> numbers = Arrays.asList(5, 2, 8, 1);
        Collections.sort(numbers, intComparator);

        // Output: [1, 2, 5, 8]
        System.out.println(numbers);
    }
}

Default Methods

Method
Description

Comparator<T> reversed();

Returns a comparator that reverses the order of comparison.

Comparator<T> thenComparing(Comparator<? super T> other);

Allows chaining multiple comparison criteria.

<U> Comparator<T> thenComparing(Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator);

Sorts using a key extracted from the object.

<U extends Comparable<? super U>> Comparator<T> thenComparing(Function<? super T, ? extends U> keyExtractor);

Sorts using a Comparable key extracted from the object.

Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor);

Sorts based on an int key.

Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor);

Sorts based on a long key.

Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor);

Sorts based on a double key.

import java.util.*;
import java.util.function.*;

class Employee {
    String name;
    int age;
    double salary;

    Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return name + " (Age: " + age + ", Salary: " + salary + ")";
    }
}

public class ComparatorExample {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("Alice", 30, 55000),
            new Employee("Bob", 25, 50000),
            new Employee("Charlie", 30, 60000),
            new Employee("Dave", 25, 50000)
        );

        // Comparator by age
        Comparator<Employee> byAge = Comparator.comparingInt(emp -> emp.age);

        // reversed() - Reverse sorting order
        employees.sort(byAge.reversed());
        System.out.println("Sorted by Age (Descending): " + employees);
        // Output: [Alice (Age: 30, Salary: 55000.0), Charlie (Age: 30, Salary: 60000.0), Bob (Age: 25, Salary: 50000.0), Dave (Age: 25, Salary: 50000.0)]

        // thenComparing(Comparator) - First by age, then by salary
        employees.sort(byAge.thenComparing(Comparator.comparingDouble(emp -> emp.salary)));
        System.out.println("Sorted by Age, then Salary: " + employees);
        // Output: [Bob (Age: 25, Salary: 50000.0), Dave (Age: 25, Salary: 50000.0), Alice (Age: 30, Salary: 55000.0), Charlie (Age: 30, Salary: 60000.0)]

        // thenComparing(Function, Comparator) - First by age, then by name (custom comparator)
        employees.sort(byAge.thenComparing(emp -> emp.name, Comparator.naturalOrder()));
        System.out.println("Sorted by Age, then Name: " + employees);
        // Output: [Bob (Age: 25, Salary: 50000.0), Dave (Age: 25, Salary: 50000.0), Alice (Age: 30, Salary: 55000.0), Charlie (Age: 30, Salary: 60000.0)]

        // thenComparing(Function) - First by salary, then by name (natural ordering)
        employees.sort(Comparator.comparingDouble(emp -> emp.salary).thenComparing(emp -> emp.name));
        System.out.println("Sorted by Salary, then Name: " + employees);
        // Output: [Bob (Age: 25, Salary: 50000.0), Dave (Age: 25, Salary: 50000.0), Alice (Age: 30, Salary: 55000.0), Charlie (Age: 30, Salary: 60000.0)]

        // thenComparingInt(ToIntFunction) - First by salary, then by age
        employees.sort(Comparator.comparingDouble(emp -> emp.salary).thenComparingInt(emp -> emp.age));
        System.out.println("Sorted by Salary, then Age: " + employees);
        // Output: [Bob (Age: 25, Salary: 50000.0), Dave (Age: 25, Salary: 50000.0), Alice (Age: 30, Salary: 55000.0), Charlie (Age: 30, Salary: 60000.0)]

        // thenComparingLong(ToLongFunction) - (Assume a long field like employee ID, but here using age)
        employees.sort(Comparator.comparing(emp -> emp.name).thenComparingLong(emp -> emp.age));
        System.out.println("Sorted by Name, then Age: " + employees);
        // Output: [Alice (Age: 30, Salary: 55000.0), Bob (Age: 25, Salary: 50000.0), Charlie (Age: 30, Salary: 60000.0), Dave (Age: 25, Salary: 50000.0)]

        // thenComparingDouble(ToDoubleFunction) - First by age, then by salary
        employees.sort(Comparator.comparingInt(emp -> emp.age).thenComparingDouble(emp -> emp.salary));
        System.out.println("Sorted by Age, then Salary (double): " + employees);
        // Output: [Bob (Age: 25, Salary: 50000.0), Dave (Age: 25, Salary: 50000.0), Alice (Age: 30, Salary: 55000.0), Charlie (Age: 30, Salary: 60000.0)]
    }
}

Static Methods

Java 8 introduced several static methods for creating comparators.

Method
Description

Comparator<T> naturalOrder();

Returns a comparator based on natural ordering (assumes T implements Comparable<T>).

Comparator<T> reverseOrder();

Returns a comparator that reverses natural ordering.

<T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor);

Compares using a key extracted from the object.

<T, U> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator);

Compares using a key with a custom comparator.

<T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor);

Compares objects using an integer key.

<T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor);

Compares objects using a long key.

<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor);

Compares objects using a double key.

import java.util.*;
import java.util.function.*;

class Employee {
    String name;
    int age;
    double salary;

    Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return name + " (" + age + " yrs, $" + salary + ")";
    }
}

public class ComparatorExample {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("Alice", 30, 60000.50),
            new Employee("Bob", 25, 50000.75),
            new Employee("Charlie", 35, 70000.25),
            new Employee("Dave", 28, 45000.00)
        );

        // 1. Natural Order (Assumes T implements Comparable)
        employees.sort(Comparator.comparing(Employee::name));
        System.out.println("Natural Order (by name): " + employees);
        // Output: [Alice (30 yrs, $60000.5), Bob (25 yrs, $50000.75), Charlie (35 yrs, $70000.25), Dave (28 yrs, $45000.0)]

        // 2. Reverse Order
        employees.sort(Comparator.comparing(Employee::name).reversed());
        System.out.println("Reverse Order (by name): " + employees);
        // Output: [Dave (28 yrs, $45000.0), Charlie (35 yrs, $70000.25), Bob (25 yrs, $50000.75), Alice (30 yrs, $60000.5)]

        // 3. Comparing by Extracted Key (by age)
        employees.sort(Comparator.comparing(Employee::age));
        System.out.println("Sorted by Age: " + employees);
        // Output: [Bob (25 yrs, $50000.75), Dave (28 yrs, $45000.0), Alice (30 yrs, $60000.5), Charlie (35 yrs, $70000.25)]

        // 4. Comparing by Extracted Key with Custom Comparator (by name in reverse)
        employees.sort(Comparator.comparing(Employee::name, Comparator.reverseOrder()));
        System.out.println("Sorted by Name (reverse): " + employees);
        // Output: [Dave (28 yrs, $45000.0), Charlie (35 yrs, $70000.25), Bob (25 yrs, $50000.75), Alice (30 yrs, $60000.5)]

        // 5. Comparing using an Integer Key (by age)
        employees.sort(Comparator.comparingInt(Employee::age));
        System.out.println("Sorted by Age (int comparator): " + employees);
        // Output: [Bob (25 yrs, $50000.75), Dave (28 yrs, $45000.0), Alice (30 yrs, $60000.5), Charlie (35 yrs, $70000.25)]

        // 6. Comparing using a Long Key (Assume we have long ID field, here we use age for demo)
        employees.sort(Comparator.comparingLong(Employee::age));
        System.out.println("Sorted by Age (long comparator): " + employees);
        // Output: [Bob (25 yrs, $50000.75), Dave (28 yrs, $45000.0), Alice (30 yrs, $60000.5), Charlie (35 yrs, $70000.25)]

        // 7. Comparing using a Double Key (by salary)
        employees.sort(Comparator.comparingDouble(Employee::salary));
        System.out.println("Sorted by Salary (double comparator): " + employees);
        // Output: [Dave (28 yrs, $45000.0), Bob (25 yrs, $50000.75), Alice (30 yrs, $60000.5), Charlie (35 yrs, $70000.25)]
    }
}

Utility Methods in Map.Entry

The Map.Entry class provides useful comparators for sorting maps.

Method
Description

static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K, V>> comparingByValue();

Returns a comparator that compares Map.Entry objects by value.

static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp);

Compares Map.Entry objects by value using a custom comparator.

static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K, V>> comparingByKey();

Compares Map.Entry objects by key.

static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp);

Compares Map.Entry objects by key using a custom comparator.

import java.util.*;
import java.util.Map.Entry;

public class ComparatorMapExample {
    public static void main(String[] args) {
        Map<String, Integer> salaryMap = new HashMap<>();
        salaryMap.put("Alice", 60000);
        salaryMap.put("Bob", 50000);
        salaryMap.put("Charlie", 70000);
        salaryMap.put("Dave", 45000);

        List<Entry<String, Integer>> entryList = new ArrayList<>(salaryMap.entrySet());

        // 1. Comparing by Value (Natural Order)
        entryList.sort(Entry.comparingByValue());
        System.out.println("Sorted by Value (ascending): " + entryList);
        // Output: [Dave=45000, Bob=50000, Alice=60000, Charlie=70000]

        // 2. Comparing by Value (Descending Order)
        entryList.sort(Entry.comparingByValue(Comparator.reverseOrder()));
        System.out.println("Sorted by Value (descending): " + entryList);
        // Output: [Charlie=70000, Alice=60000, Bob=50000, Dave=45000]

        // 3. Comparing by Key (Natural Order)
        entryList.sort(Entry.comparingByKey());
        System.out.println("Sorted by Key (ascending): " + entryList);
        // Output: [Alice=60000, Bob=50000, Charlie=70000, Dave=45000]

        // 4. Comparing by Key (Descending Order)
        entryList.sort(Entry.comparingByKey(Comparator.reverseOrder()));
        System.out.println("Sorted by Key (descending): " + entryList);
        // Output: [Dave=45000, Charlie=70000, Bob=50000, Alice=60000]
    }
}

Last updated

Was this helpful?