Skip to content

Intermediate · The collections framework · topic 34 of 60 · 25 min

Sorting with Comparable and Comparator

Explanation

Sorting needs a rule for which object comes first. A class can define its natural order by implementing Comparable (compareTo). For any other order, pass a Comparator, most easily built with Comparator.comparing(...).thenComparing(...).reversed(). Sort with list.sort(comparator) or Collections.sort.

Natural order with Comparable

Strings sort alphabetically and numbers numerically because those classes implement Comparable. compareTo(other) returns a negative number if this comes first, zero if equal, and a positive number if this comes after.

java
public record Employee(String name, String dept, int salary) implements Comparable<Employee> {
    @Override
    public int compareTo(Employee other) {
        return this.name.compareTo(other.name);      // natural order: by name
    }
}
List<Employee> staff = new ArrayList<>(List.of(
    new Employee("Ravi", "IT", 65000),
    new Employee("Asha", "HR", 52000),
    new Employee("Meena", "IT", 78000)));
Collections.sort(staff);              // uses compareTo: Asha, Meena, Ravi

Other orders with Comparator

A class has only one natural order, but reports need many: by salary, by department then name, highest first. A Comparator is a separate object (often a lambda) that compares two objects. Comparator factory methods make this readable.

java
staff.sort(Comparator.comparingInt(Employee::salary));                 // lowest salary first
staff.sort(Comparator.comparingInt(Employee::salary).reversed());      // highest first
staff.sort(Comparator.comparing(Employee::dept)
                     .thenComparing(Employee::name));                  // by dept, then name
staff.sort((a, b) -> Integer.compare(b.salary(), a.salary()));        // a lambda by hand
staff.sort(Comparator.comparing(Employee::name, String.CASE_INSENSITIVE_ORDER));

Sorting arrays and other collections

java
String[] cities = {"pune", "Agra", "delhi"};
Arrays.sort(cities, String.CASE_INSENSITIVE_ORDER);     // [Agra, delhi, pune]
TreeSet<Employee> bySalary = new TreeSet<>(Comparator.comparingInt(Employee::salary));
PriorityQueue<Employee> queue = new PriorityQueue<>(Comparator.comparingInt(Employee::salary).reversed());
Careful: Do not write comparators as return a.salary() - b.salary(); when values can be large or negative, the subtraction can overflow and give the wrong sign. Use Integer.compare(a, b) or Comparator.comparingInt. Also, a Comparator used by a TreeSet or TreeMap decides equality: two different employees with the same salary would be treated as duplicates and one would vanish.

Handling nulls and keys that are missing

If some values can be null (for example an employee with no department yet), comparing them throws NullPointerException. Comparator.nullsFirst(...) and nullsLast(...) wrap another comparator and place nulls at the start or the end instead.

java
staff.sort(Comparator.comparing(Employee::dept, Comparator.nullsLast(Comparator.naturalOrder())));
// employees without a department are listed at the end instead of crashing the sort

Stable sorting

Java's object sort (List.sort, Collections.sort) is stable: elements that compare as equal keep their original relative order. That is why sorting by name and then sorting by department gives department groups with names still in order, although thenComparing is clearer.

Think of it like this: Comparable is like a person's height, which is a natural way to line up students. A Comparator is like the PT teacher deciding today to line them up by roll number, or by house colour then height. Same students, different rules.
Tip: Prefer Comparator.comparing chains over hand-written compare methods: they are shorter, avoid overflow bugs, and read like the requirement ("by department, then by salary descending").

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Sorting with Comparable and Comparator · 1/6

Comparable and Comparator

In this lesson you will sort objects in any order you need.

Prefer a video by a teacher? Find free videos on “java comparable vs comparator comparing thenComparing reversed” on YouTube ↗

In real life: Ranking delivery partners

A delivery platform ranks partners for incentives: highest rating first, then most deliveries, then name for a stable tie-break. One comparator chain expresses the whole policy.

java
record Partner(String name, double rating, int deliveries) {}
Comparator<Partner> ranking = Comparator.comparingDouble(Partner::rating).reversed()
        .thenComparing(Comparator.comparingInt(Partner::deliveries).reversed())
        .thenComparing(Partner::name);
partners.sort(ranking);

Practice (0/3 done)

Task 1 · Question

List<String> l = new ArrayList<>(List.of("bb", "a", "ccc")); l.sort(Comparator.comparing(String::length).reversed()); What is l?

Task 2 · Code · Javascript

The same comparator chain in JavaScript, which runs here: write rankStaff(staff) that returns a NEW array sorted by dept A–Z, then by salary highest first, then by name A–Z. Items are { name, dept, salary }.

Task 3 · Practice

Create a record Product(String name, double price, int rating). Make it Comparable by name. Then sort a list of products (a) by natural order, (b) by price lowest first, (c) by rating highest first then price lowest first, printing the list each time.

Quick quiz

Question 1

1.a.compareTo(b) returns a negative number. What does it mean?

Question 2

2.How many natural orders can a class have?

Question 3

3.Why avoid (a, b) -> a.x - b.x?

Question 4

4.Is List.sort stable?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

a.compareTo(b) returns a negative number. What does it mean?

Answer: a comes before b

Negative means a sorts first.

How many natural orders can a class have?

Answer: One (Comparable)

Other orders use Comparators.

Why avoid (a, b) -> a.x - b.x?

Answer: Subtraction can overflow and give the wrong sign

Use Integer.compare.

Is List.sort stable?

Answer: Yes, equal elements keep their order

Java's object sort is stable.

Which key sorts (name, marks) tuples by marks high to low, then name A to Z?Related

Answer: lambda s: (-s[1], s[0])

The tuple compares marks first (negated for high to low), then the name.

From Sorting in real code: keys, comparators, counting sort

What does it mean that Python sorting is stable?Related

Answer: Items with equal keys keep their original order

A stable sort keeps the earlier of two equal items first.

From Sorting with key

Which Set keeps insertion order?Related

Answer: LinkedHashSet

LinkedHashSet remembers insertion order.

From Sets: HashSet, LinkedHashSet and TreeSet

A PriorityQueue<Integer> created with no comparator gives…Related

Answer: The smallest first

The default is a min-heap by natural order.

From Queues, stacks and priority queues

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “The collections framework” before its exam: Queues, stacks and priority queues.
  2. Module 7 exam: The collections framework · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Lambdas, streams and files. 60 topics left in the course before the final exam.

← Previous: Maps: HashMap, LinkedHashMap and TreeMap