Part 10 — Comparable vs Comparator Deep Dive¶
This topic is asked in almost every senior Java interview because sorting is fundamental.
Typical interview questions:
- What is the difference between
ComparableandComparator? - When should you use each?
- How does
Collections.sort()work internally? - What is TimSort?
- What happens if
compareTo()is implemented incorrectly? - Why should a comparator be consistent with
equals()?
1. Why Do We Need Sorting?¶
Consider:
List<Integer> list =
Arrays.asList(30, 10, 20);
Without sorting:
30 10 20
After sorting:
10 20 30
Java provides multiple ways to define the ordering.
2. Comparable¶
Comparable defines the natural ordering of a class.
Interface:
public interface Comparable<T> {
int compareTo(T other);
}
Example:
class Employee implements Comparable<Employee> {
private int id;
Employee(int id) {
this.id = id;
}
@Override
public int compareTo(Employee other) {
return Integer.compare(this.id, other.id);
}
}
Now:
Collections.sort(employeeList);
works automatically.
3. How compareTo() Works¶
Rule:
Negative → this < other
Zero → equal
Positive → this > other
Example:
5.compareTo(10)
Result:
-1
Meaning:
5 comes before 10
4. Natural Ordering Examples¶
Integer¶
Collections.sort(numbers);
Ascending.
String¶
Collections.sort(names);
Alphabetical.
LocalDate¶
Collections.sort(dates);
Chronological.
5. Comparator¶
Comparator defines an external ordering.
Interface:
public interface Comparator<T> {
int compare(T o1, T o2);
}
Unlike Comparable, you do not modify the class.
6. Example¶
Employee:
class Employee {
int id;
double salary;
}
Sort by salary:
Comparator<Employee> bySalary =
Comparator.comparingDouble(Employee::getSalary);
employees.sort(bySalary);
The class itself remains unchanged.
7. Comparable vs Comparator¶
| Comparable | Comparator |
|---|---|
| Inside class | Outside class |
| One natural ordering | Many possible orderings |
compareTo() | compare() |
| Modifies class | Does not modify class |
8. Multiple Sorting Strategies¶
Employee:
ID
Name
Salary
Age
Possible comparators:
Comparator<Employee> byId
Comparator<Employee> byName
Comparator<Employee> bySalary
Comparator<Employee> byAge
One class.
Many orderings.
9. Lambda Comparator¶
Before Java 8:
Collections.sort(list,
new Comparator<Employee>() {
@Override
public int compare(
Employee a,
Employee b) {
return a.getAge() - b.getAge();
}
});
Java 8:
list.sort(
Comparator.comparingInt(Employee::getAge)
);
Cleaner and safer.
10. Reverse Order¶
Ascending:
Comparator.comparing(Employee::getSalary)
Descending:
Comparator.comparing(Employee::getSalary)
.reversed();
11. Multi-Level Sorting¶
Very common interview topic.
Sort by:
- Department
- Salary
- Name
Comparator<Employee> comparator =
Comparator
.comparing(Employee::getDepartment)
.thenComparing(Employee::getSalary)
.thenComparing(Employee::getName);
Example:
Before:
IT 5000 Bob
IT 5000 Alice
IT 4000 Tom
After:
IT 4000 Tom
IT 5000 Alice
IT 5000 Bob
12. Comparator Chaining¶
Useful methods:
thenComparing()
reversed()
nullsFirst()
nullsLast()
Example:
Comparator<String> cmp =
Comparator.nullsLast(String::compareTo);
13. Sorting with Null Values¶
Without handling null:
Collections.sort(list);
May throw:
NullPointerException
Safe:
Comparator<String> comparator =
Comparator.nullsFirst(
Comparator.naturalOrder()
);
14. Comparable Contract¶
The contract requires:
Reflexive¶
a.compareTo(a) == 0
Symmetric Sign¶
If:
a.compareTo(b) < 0
Then:
b.compareTo(a) > 0
Transitive¶
If:
A < B
B < C
Then:
A < C
Violating these rules can cause unpredictable behavior in sorted collections and sorting algorithms.
15. Comparator Consistency with equals()¶
Interview favorite.
Suppose:
Comparator<String> lengthComparator =
Comparator.comparingInt(String::length);
Now:
TreeSet<String> set =
new TreeSet<>(lengthComparator);
set.add("cat");
set.add("dog");
Result:
cat
Why?
Both have:
Length = 3
Comparator returns:
0
TreeSet considers them duplicates.
This is why comparators should usually be consistent with equals().
16. Collections.sort()¶
Older style:
Collections.sort(list);
Internally:
List.sort(...)
In modern JDKs, Collections.sort() delegates to List.sort().
17. List.sort()¶
Modern approach:
list.sort(
Comparator.comparing(Employee::getAge)
);
Preferred over Collections.sort() for readability.
18. TimSort¶
Interview favorite.
Java uses:
TimSort
for sorting objects.
Invented by:
Tim Peters
Originally for Python.
19. Why TimSort?¶
TimSort combines:
Merge Sort
+
Insertion Sort
Advantages:
- Stable
- Fast on partially sorted data
- Excellent real-world performance
20. Stable Sorting¶
Suppose:
Before:
Alice 25
Bob 25
Tom 30
Sort by age.
Result:
Alice 25
Bob 25
Tom 30
Alice remains before Bob.
Relative order of equal elements is preserved.
This is called a stable sort.
21. Primitive Arrays¶
Arrays.sort(int[]);
Does not use TimSort.
It uses a Dual-Pivot Quicksort implementation.
22. Object Arrays¶
Arrays.sort(Employee[]);
Uses:
TimSort
23. Complexity¶
| Algorithm | Best | Average | Worst |
|---|---|---|---|
| TimSort | O(n) | O(n log n) | O(n log n) |
| Dual-Pivot Quicksort (primitive arrays) | O(n log n) | O(n log n) | O(n²) |
TimSort's best case is O(n) because it detects already sorted or partially sorted runs.
24. Common Mistake¶
Wrong:
return this.salary - other.salary;
Problems:
- Overflow for integers
- Impossible for doubles
Correct:
return Double.compare(
this.salary,
other.salary
);
Similarly:
Integer.compare(a, b)
Long.compare(a, b)
25. Production Examples¶
Sort by salary:
employees.sort(
Comparator.comparingDouble(Employee::getSalary)
);
Descending:
employees.sort(
Comparator
.comparingDouble(Employee::getSalary)
.reversed()
);
Multiple fields:
employees.sort(
Comparator
.comparing(Employee::getDepartment)
.thenComparing(Employee::getAge)
.thenComparing(Employee::getName)
);
Common Interview Questions¶
Q1. Difference between Comparable and Comparator?¶
Comparable defines the natural ordering inside the class.
Comparator defines external orderings.
Q2. Can a class have multiple Comparables?¶
No.
A class can implement Comparable only once.
Multiple sorting strategies require different Comparator implementations.
Q3. Which is better?¶
Use:
Comparablefor the default/natural ordering.Comparatorfor alternative orderings.
Often, a class has one Comparable implementation and many Comparators.
Q4. Which algorithm does Java use?¶
Objects:
TimSort
Primitive arrays:
Dual-Pivot Quicksort
Q5. Why is TimSort used?¶
Because it is:
- Stable
- Adaptive
- Efficient for partially sorted data
- O(n log n) worst case
Q6. What happens if compareTo() returns inconsistent values?¶
Results can include:
- Incorrect sorting
- Unexpected behavior in
TreeMap/TreeSet - Violated collection invariants
Q7. Why should a comparator be consistent with equals()?¶
Because sorted collections (TreeSet, TreeMap) treat elements as duplicates when the comparator returns 0.
Q8. Is sorting stable?¶
For object sorting with TimSort:
Yes.
For primitive arrays:
The Dual-Pivot Quicksort implementation is not stable.
Q9. Difference between Collections.sort() and Arrays.sort()?¶
| Collections.sort() | Arrays.sort() |
|---|---|
Works on List | Works on arrays |
Delegates to List.sort() | Sorts array directly |
Q10. Which sorting method should you use in Java 8+?¶
Prefer:
list.sort(comparator);
It is the modern, idiomatic API.
Senior Interview Scenario¶
Question:
Why does this TreeSet contain only one element?
TreeSet<String> set =
new TreeSet<>(
Comparator.comparingInt(String::length)
);
set.add("cat");
set.add("dog");
Answer:
The comparator returns 0 because both strings have length 3.
TreeSet determines uniqueness using the comparator, not equals().
Therefore, "dog" replaces the logical position of "cat" and is treated as a duplicate.
Key Takeaways¶
Comparabledefines a class's natural ordering.Comparatordefines external and reusable sorting strategies.- Comparator chaining (
thenComparing) is common in production code. - Use
Integer.compare(),Long.compare(), andDouble.compare()instead of subtraction. - Java uses TimSort for object sorting and Dual-Pivot Quicksort for primitive arrays.
- TimSort is stable and adaptive.
- A comparator returning
0makesTreeSetandTreeMaptreat two keys as equivalent.
Next: Part 11 — Immutable Collections (Java 9+)¶
We'll cover:
List.of(),Set.of(),Map.of()- Immutable vs unmodifiable collections
Collections.unmodifiableList()- Defensive copying
- Performance and memory benefits
- Production use cases
- Common interview questions