Java > Java Collections Framework > Iterators and Streams > Sorting Collections with Comparator

Sorting a List of Objects Using Comparator

This snippet demonstrates how to sort a list of custom objects using a `Comparator` in Java. We define a `Person` class and then create a `Comparator` to sort a list of `Person` objects based on their age. This approach provides flexibility in defining different sorting criteria.

Code Example

This code creates a `Person` class with `name` and `age` attributes. It then creates a list of `Person` objects and sorts them using `Collections.sort()` along with a custom `Comparator`. The first sorting operation sorts by age using `Comparator.comparingInt(Person::getAge)`, and the second sorts by name using `Comparator.comparing(Person::getName)`. Method references `Person::getAge` and `Person::getName` are used to provide the sorting key.

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

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 + "}";
    }
}

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("Charlie", 35));

        System.out.println("Before sorting: " + people);

        // Sort by age using a Comparator
        Collections.sort(people, Comparator.comparingInt(Person::getAge));

        System.out.println("After sorting by age: " + people);

        // Sort by name using a Comparator
        Collections.sort(people, Comparator.comparing(Person::getName));

        System.out.println("After sorting by name: " + people);
    }
}

Concepts Behind the Snippet

The `Comparator` interface in Java is used to define a comparison function for objects of a specific type. The `Collections.sort()` method can accept a `Comparator` as an argument to customize the sorting behavior. `Comparator.comparingInt()` and `Comparator.comparing()` are static factory methods that create comparators based on the result of a function applied to each element. Method references provide a concise way to specify the function.

Real-Life Use Case

Sorting a list of employees based on salary in a payroll system or sorting a list of products based on price in an e-commerce application are real-world examples of using `Comparator` for sorting.

Best Practices

  • Use method references whenever possible for concise and readable code.
  • Consider creating reusable `Comparator` instances for common sorting criteria.
  • Handle null values gracefully within the `Comparator`. Consider using `Comparator.nullsFirst()` or `Comparator.nullsLast()`.

Interview Tip

Be prepared to explain the difference between `Comparable` and `Comparator`. `Comparable` is implemented by the class whose objects you want to compare, while `Comparator` is a separate class that defines a comparison function. `Comparable` allows a class to define its natural ordering, whereas `Comparator` allows for multiple or situational sorting strategies.

When to Use Them

Use `Comparator` when you need to sort a collection of objects based on criteria other than their natural ordering (defined by `Comparable`), or when you don't have access to modify the class of the objects you want to sort.

Memory Footprint

The memory footprint of using `Comparator` is generally small. The `Comparator` instance itself occupies a small amount of memory. The sorting process may require temporary memory depending on the sorting algorithm used by `Collections.sort()`, but this is independent of the `Comparator`.

Alternatives

An alternative to `Comparator` is to use the `Comparable` interface and implement the `compareTo()` method in the `Person` class itself if the sorting criteria represent the natural ordering. However, `Comparator` is preferable when you need multiple sorting strategies or don't control the source code of the class.

Pros

  • Flexibility: Allows for sorting based on multiple criteria.
  • Reusability: `Comparator` instances can be reused for multiple sorting operations.
  • Decoupling: Separates the sorting logic from the class being sorted.

Cons

  • Increased complexity: May require writing additional code for the `Comparator`.
  • Slight overhead: Introducing an additional class/object (the Comparator).

FAQ

  • What is the difference between `Comparable` and `Comparator`?

    `Comparable` is an interface that allows a class to define its natural ordering, by implementing the `compareTo()` method. `Comparator` is a separate interface that defines a comparison function for objects of a specific type. Use `Comparable` when a class has a natural ordering. Use `Comparator` when you need multiple sorting criteria or don't control the class's source code.
  • Can I use a lambda expression to define a `Comparator`?

    Yes, you can use lambda expressions to define `Comparator` instances in a concise way. For example: `Comparator ageComparator = (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge());`
  • How do I sort in descending order using a Comparator?

    You can use the `reversed()` method of the `Comparator` interface. For example: `Collections.sort(people, Comparator.comparingInt(Person::getAge).reversed());`