Comparable & Comparator

Java can sort numbers and strings out of the box — but how does it sort your objects? You teach it. Comparable gives a class one built-in order; Comparator gives you unlimited orders on the side. Together they power every sort, TreeMap , and priority queue in Java.

Learn Comparable & Comparator in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You'll want interfaces (Comparable is one you implement), lists to sort, and a glance at lambdas — modern comparators read best as lambdas and method references.

💡 Analogy: Think of a deck of playing cards. Comparable is the order printed on the cards themselves — the natural rank, 2 through Ace. Everyone agrees on it because it's baked in. A Comparator is a house rule you bring to a specific game: "tonight, sort by suit colour, then by rank." The cards don't change — you just supply a different way to compare them whenever you sort.

So a type has at most one Comparable order (its compareTo), but you can write as many Comparators as you like. And crucially, a Comparator lets you sort classes you can't even edit — like library types.

A class implements Comparable<T> and overrides one method, compareTo . That defines the type's natural order — the order used by Collections.sort(list) , list.sort(null) , TreeSet , and TreeMap when you don't supply anything else.

The compareTo contract: return a negative number if this comes before other , zero if they tie, and a positive number if this comes after. Implement it with helpers like Integer.compare or Double.compare — never raw subtraction.

A Comparator defines an order outside the class, so you can have many and switch between them per sort. The modern way to build one is the Comparator.comparing(...) family, which extracts a sort key from each object:

Pass the comparator to list.sort(comparator) and you're done — no change to the class itself.

Real-world sorts have tie-breakers: sort by last name, then first name, then age. Chain comparators with thenComparing — it only takes effect when the previous keys are equal, so it cleanly resolves ties.

Read it top to bottom as "primary key, then secondary, then tertiary." Each level is consulted only when all the levels above it tie.

Java ships with handy ready-made comparators so you don't reinvent them:

These lines sort a list of employees by department, then by salary descending within each department. Put them in order.

Start the sort call, give the primary key (department), chain the tie-breaker (salary, reversed for descending), close the call, then print. Reversing only the inner comparator keeps department ascending.

Predict the output before opening each answer.

Answer: [8, 5, 2, 1] . reverseOrder() sorts by the natural order (ascending) and then flips it, giving largest-first.

Answer: [Alice, bob, carol] . The natural order for String is case-sensitive and compares by Unicode value, where uppercase letters (A=65) come before lowercase (b=98). That's why "Alice" sorts first. Use CASE_INSENSITIVE_ORDER for human-friendly sorting.

Answer: negative . 3.compareTo(9) means "is 3 before 9?" — yes, so it returns a negative number (this < other). That negative result is exactly what tells a sort to keep 3 ahead of 9.

🎯 Your Turn #1 — Natural order for Book

Implement compareTo so books sort shortest-first by page count.

🎯 Your Turn #2 — Price then name

Build a comparator that sorts by price ascending and breaks ties by name.

🧩 Mini-Challenge — Leaderboard

Sort players by score descending, break ties by name ascending, and print with rank numbers.

You can now give a class its natural order with Comparable , build any number of ad-hoc orders with Comparator , chain tie-breakers with thenComparing , reverse and null-proof them — and you'll never write a buggy a - b comparator again.

Next up: Records — a concise way to model immutable data that pairs perfectly with comparators.

Practice quiz

What is the core difference between Comparable and Comparator?

  • Comparable is external; Comparator is implemented by the class
  • They are identical
  • Comparable defines one natural order inside the class; Comparator is an external order you can have many of
  • Comparator can only sort numbers

Answer: Comparable defines one natural order inside the class; Comparator is an external order you can have many of. Comparable's compareTo is the single natural order; Comparators live outside the class so you can write as many as you like.

compareTo (or a comparator) returns a NEGATIVE number when:

  • this comes before (is less than) other
  • this is greater than other
  • this equals other
  • Always, on error

Answer: this comes before (is less than) other. Negative means this < other, zero means equal, positive means this > other.

Why should you NEVER write 'return a - b' in a comparator?

  • It is slower
  • It does not compile
  • It only works for doubles
  • Integer subtraction can overflow and silently reverse the order

Answer: Integer subtraction can overflow and silently reverse the order. For large/opposite-sign values a - b wraps around. Use Integer.compare(a, b), which handles the full range.

List<Integer> nums = List.of(5,2,8,1); after nums.sort(Comparator.reverseOrder()) the list is:

reverseOrder() flips the natural ascending order, producing largest-first: [8, 5, 2, 1].

Sorting ["bob", "Alice", "carol"] with Comparator.naturalOrder() gives:

String natural order is case-sensitive by Unicode value; uppercase 'A' (65) sorts before lowercase 'b' (98).

How do you add a tie-breaker key to a comparator?

  • .andThen(...)
  • .orElse(...)
  • .thenComparing(...)
  • .plus(...)

Answer: .thenComparing(...). thenComparing chains another key that is consulted only when the previous keys are equal.

Calling .reversed() on a chained comparator comparing(a).thenComparing(b).reversed() reverses:

  • Only key b
  • Both keys (everything before it)
  • Only key a
  • Nothing

Answer: Both keys (everything before it). reversed() flips the whole comparator built so far. To reverse only one key, reverse that key's comparator alone.

Which built-in comparator sorts strings alphabetically while ignoring case?

  • Comparator.naturalOrder()
  • Comparator.reverseOrder()
  • Comparator.nullsFirst()
  • String.CASE_INSENSITIVE_ORDER

Answer: String.CASE_INSENSITIVE_ORDER. String.CASE_INSENSITIVE_ORDER compares ignoring case, giving human-friendly ordering.

Sorting a list that contains a null element with the natural order will:

  • Put nulls last automatically
  • Throw a NullPointerException
  • Skip the nulls
  • Sort fine

Answer: Throw a NullPointerException. Natural-order sorting can't compare null. Wrap with Comparator.nullsFirst(...) or nullsLast(...) to handle nulls.

If compareTo returns 0 for objects that are NOT equals(), a TreeSet will:

  • Throw an exception
  • Keep both anyway
  • Treat them as duplicates and drop one
  • Sort them randomly

Answer: Treat them as duplicates and drop one. Sorted collections use compareTo for identity; a 0 result means 'same key', so the second object is treated as a duplicate.