How to create this java comparator
Categories:
Mastering Java Comparators: Custom Sorting for Any Object

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 beforeo2
. - Zero: If
o1
ando2
are considered equal for sorting purposes. - Positive integer: If
o1
should come aftero2
.
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.
int
, double
, etc., always use their respective wrapper class's static compare()
method (e.g., Integer.compare(int a, int b)
) to avoid potential overflow issues that can arise from simple subtraction (a - b
).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 aComparator
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.
Comparator.comparing()
method is particularly powerful as it takes a Function
to extract the key to be compared. This eliminates the need for boilerplate compare
method implementations for simple single-field comparisons.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)
.