Skip to Content
Course content

48: Comparators and Comparable

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've noticed a recurring pattern when developers first hit sorting in Java: they treat Comparable and Comparator as if they are two different names for the same thing. They'll spend an hour trying to figure out why they can't "just add another Comparable" to a class so they can sort a list of objects by three different fields. If you've tried to implement multiple Comparable interfaces on a single class, you've probably realized the compiler simply won't let you. You can't inherit from the same interface twice with different generic types.

Thinking Comparable and Comparator are Interchangeable

Here is the concrete reason why that mindset fails: Comparable defines the natural ordering of an object. A Movie object might naturally be sorted by its release year. That's its "default" state. But what happens when your user clicks a column header in your UI to sort those movies by rating, or alphabetically by title? You can't change the "natural" identity of the Movie class every time a user clicks a button.

If you try to force everything into Comparable, you're essentially saying, "This object has one, and only one, way to be compared to another object of its type." In the real world, that is almost never true.

Defining the "Natural" Order with Comparable

When you implement Comparable<T>, you are modifying the class itself. You're telling Java: "I know what this object is, and I know how it should behave by default when put into a TreeSet or passed to Collections.sort()."

public class Movie implements Comparable<Movie> {
    private String title;
    private int releaseYear;

    public Movie(String title, int releaseYear) {
        this.title = title;
        this.releaseYear = releaseYear;
    }

    @Override
    public int compareTo(Movie other) {
        // Natural order: Oldest movies first
        return Integer.compare(this.releaseYear, other.releaseYear);
    }

    public String getTitle() { return title; }
    public int getReleaseYear() { return releaseYear; }
}

Now, any list of Movie objects can be sorted simply by calling Collections.sort(movieList). Java looks at the class, sees it implements Comparable, and uses compareTo. Simple, right? But we're still stuck with that release year logic.

Breaking the Rules with Comparator

This is where Comparator comes in. Think of a Comparator as a separate "judge" that stands outside the class. The Movie class doesn't need to know the Comparator exists. This allows you to define as many different sorting strategies as you want without touching the original class code.

In modern Java, we rarely write full classes for this. We use lambda expressions or the Comparator.comparing static methods, which are far more readable.

import java.util.*;

public class MovieSorter {
    public static void main(String[] args) {
        List<Movie> movies = new ArrayList<>();
        movies.add(new Movie("Inception", 2010));
        movies.add(new Movie("The Godfather", 1972));
        movies.add(new Movie("Interstellar", 2014));

        // 1. Use Natural Order (Comparable)
        Collections.sort(movies); 

        // 2. Use a custom Comparator to sort by title alphabetically
        movies.sort(Comparator.comparing(Movie::getTitle));
        
        // 3. Need a complex one? Write a lambda.
        // Sort by title length, then by year
        movies.sort((m1, m2) -> {
            int lenCompare = Integer.compare(m1.getTitle().length(), m2.getTitle().length());
            return lenCompare != 0 ? lenCompare : m1.compareTo(m2);
        });
    }
}

I personally prefer the Comparator.comparing syntax whenever possible. It's declarative. It tells the next developer what you are sorting by, rather than forcing them to parse the subtraction or comparison logic inside a method body. Just remember: Comparable is for the object's own identity; Comparator is for external requirements.




📋 Practical Task

Build a Multi-Criteria Game Library Sorter

You are building a digital game library. You have a Game class with the following fields: String title, int releaseYear, and double metacriticScore.

Your task:

  • Implement Comparable<Game> in the Game class so that the natural order is based on the releaseYear (ascending).
  • Create a GameLibrary class with a main method that populates a List<Game> with at least four different games.
  • Demonstrate three different sorts on that list:
    1. Sort by natural order.
    2. Sort by metacriticScore in descending order (highest score first).
    3. Sort by title alphabetically.
  • Print the list to the console after each sort to verify the results.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.