Q1. Difference between Comparable and comparator
Comparable = "How should this object normally be sorted?"
Comparator = "How do I want to sort it right now?"
public class Employee implements Comparable<Employee> {
private int id;
private String name;
private double salary;
public Employee(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
@Override
public int compareTo(Employee other) {
return Integer.compare(this.id, other.id);
}
@Override
public String toString() {
return id + " " + name;
}
}
Output:
101 Tom
102 David
103 John
Because the class itself says:
compareTo() => sort by id
This becomes the natural ordering.
Why Comparable Can Become a Problem
Imagine HR wants:
Sort by salary
Finance wants:
Sort by employee id
Manager wants:
Sort by name
With Comparable you can only have ONE natural ordering.
You cannot implement:
compareToBySalary()
compareToByName()
compareToByDepartment()
Only one compareTo() exists.
That's where Comparator comes in.
Comparator - Sort by salary:
employees.sort(
Comparator.comparing(Employee::getSalary)
);
Sort by name:
employees.sort(
Comparator.comparing(Employee::getName)
);
Sort by id descending:
employees.sort(
Comparator.comparing(Employee::getId)
.reversed()
);
No need to modify Employee class.
Sort by age then name
employees.sort(
Comparator.comparing(Employee::getAge)
.thenComparing(Employee::getName)
);
What Happens Internally?
Comparable:
Collections.sort(list);
Java internally calls:
obj1.compareTo(obj2);
Comparator:
Collections.sort(list, comparator);
Java internally calls:
comparator.compare(obj1, obj2);
Why? if I use Comparator it does not require to implement compare() in the Employee class but if I use Comparable interface I need to override compareTo method?
Comparable = Object Knows How To Compare Itself
public class Employee
implements Comparable<Employee> {
@Override
public int compareTo(Employee other) {
return Integer.compare(this.id, other.id);
}
}
you are telling Java:
Every Employee knows how to compare itself with another Employee.
Then Java can do:
e1.compareTo(e2);
because the method exists inside Employee.
Comparator = Separate Comparison Object
No Comparable. No compareTo().
Now you write:
Comparator<Employee> comparator =
Comparator.comparing(Employee::name);
Java now has an external object:
Comparator<Employee>
whose job is: compare(e1, e2)
The comparison logic is no longer inside Employee.
Why Employee Doesn't Need to Implement Comparator
Because Comparator is not part of Employee.
Comparator<Employee> byName =
Comparator.comparing(Employee::name);
Comparator<Employee> bySalary =
Comparator.comparing(Employee::salary);
Now you have two different comparison strategies.
Employee remains unchanged.
Why We Rarely Do This
Technically you can write:
public class Employee
implements Comparator<Employee> {
@Override
public int compare(Employee e1,
Employee e2) {
return e1.id - e2.id;
}
}
But this is bad design.
Now Employee is:
An employee AND A comparison engine
Two responsibilities.
Violates the Single Responsibility Principle.
If String implementation it is using "Tom".compareTo("John") then how we able to sort List? where do it iterate ?
You write:
employees.sort( Comparator.comparing(Employee::name));
and wonder:
If Java is doing "Tom".compareTo("John"), who is iterating through the List?
The answer is:
The sorting algorithm inside List.sort() **(TimSort in modern Java) **iterates through the list and repeatedly calls the comparator.
Step 1: Your List
Suppose:
List<Employee> employees = List.of(
new Employee(101, "Tom", 70000),
new Employee(102, "David", 80000),
new Employee(103, "John", 90000)
);
Initial:
[Tom, David, John]
Step 2: Create Comparator
This: Comparator.comparing(Employee::name)
creates something similar to:
Comparator<Employee> comparator =
(e1, e2) -> e1.name().compareTo(e2.name());
Step 3: Java Starts Sorting
When you call: employees.sort(comparator);
Java internally does something like:
TimSort.sort(employees, comparator);
You don't see this code, but the sorting algorithm repeatedly asks:
comparator.compare(e1, e2)
Step 4: Actual Comparisons
Suppose the algorithm picks:
Employee(101, "Tom")
Employee(102, "David")
and calls:
comparator.compare(tom, david);
which executes:
tom.name().compareTo(david.name())
becomes: "Tom".compareTo("David")
Result:
positive number
Meaning: Tom should come after David
Next comparison: comparator.compare(tom, john);
becomes: "Tom".compareTo("John")
Result: positive number
Meaning: Tom should come after John
Next comparison:
comparator.compare(david, john);
becomes: "David".compareTo("John")
Result: negative number
Meaning: David should come before John
Final order:
David
John
Tom
Important Point - You are NOT iterating.
You only provide: Comparator
Java's sorting algorithm does the iteration.
Think of it like this:
You:
Here is the rule for comparing employees.
Java:
Thanks. I'll repeatedly apply that rule
while rearranging the list.
Similar to Collections.sort()
When you write:
Collections.sort(employees, comparator);
internally Java does something similar to:
for (...) {
if (comparator.compare(a, b) > 0) {
swap(a, b);
}
}
The real algorithm is TimSort (much more sophisticated), but conceptually it's repeatedly:
compare()
compare()
compare()
compare()
swap()
compare()
...
How Many Times Is compare() Called?
For 3 employees:
Tom
David
John
you might see:
Comparator<Employee> comparator =
(e1, e2) -> {
System.out.println(
"Comparing " + e1.name()
+ " and "
+ e2.name()
);
return e1.name().compareTo(e2.name());
};
Output could be:
Comparing David and Tom
Comparing John and David
Comparing John and Tom
Comparing John and David
Notice:
Java chooses which elements to compare.
The sorting algorithm controls the iteration.
Your comparator only provides the comparison logic.
Senior Developer Mental Model
When you write:
Comparator.comparing(Employee::name)
think of it as generating:
(Employee e1, Employee e2)
-> e1.name().compareTo(e2.name())
Then: employees.sort(...)
means:
Java sorting algorithm
+
Your comparison rule
=
Sorted list
The iteration is performed by the sorting algorithm (TimSort), not by Comparator, Comparable, or your Employee class.
VERY IMPORTANT OBSERVATION - DEEP DRIVE
my
List<Employee> employees = new java.util.ArrayList<>(List.of(
new Employee(103, "John", 90000),
new Employee(101, "Tom", 70000),
new Employee(104, "Tom", 80000),
new Employee(102, "David", 80000)
));
and
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
System.out.println(
"Comparing " + o1.name()
+ " and "
+ o2.name()
);
return o1.name().compareTo(o2.name());
}
});
It prints : But why?
Comparing Tom and John
Comparing Tom and Tom
Comparing David and Tom
Comparing David and Tom
Comparing David and John
*This is a fantastic observation because it reveals how Java's sorting algorithm works.
*
Your list is:
[
John,
Tom,
Tom,
David
]
and your comparator prints:
Comparing Tom and John
Comparing Tom and Tom
Comparing David and Tom
Comparing David and Tom
Comparing David and John
You expected something like:
John vs Tom
Tom vs Tom
Tom vs David
...
but sorting algorithms do not compare elements in a simple left-to-right order.
What's Actually Happening?
When you call: employees.sort(comparator);
Java uses TimSort (since Java 7).
TimSort is:
Stable
Optimized for partially sorted data
Not a simple bubble sort
Therefore:
- You should never assume the order of compare() calls.
- The sorting algorithm decides:
- Which elements to compare
- How many times to compare
- Whether to compare the same elements again
Let's Analyze Your Output
Initial list:
Index 0 -> John
Index 1 -> Tom
Index 2 -> Tom
Index 3 -> David
Comparison 1
Comparing Tom and John
Internally: "Tom".compareTo("John")
Result: positive
Meaning: Tom should come after John
Comparison 2
Comparing Tom and Tom
Second Tom compared with first Tom.
"Tom".compareTo("Tom")
Result: 0
Meaning: equal
Comparison 3
Comparing David and Tom
"David".compareTo("Tom")
Result: negative
Meaning: David comes before Tom
Comparison 4
Comparing David and Tom
Again!
Many developers find this surprising.
But sorting algorithms often compare the same elements multiple times.
There is no guarantee: compare(a,b) called only once
In fact:
compare(a,b)
compare(a,b)
compare(a,b)
can happen.
This is why comparators must be:
- Pure
- Stateless
- No side effects
Comparison 5
Comparing David and John
"David".compareTo("John")
Result: negative
Meaning: David comes before John
Final Sorted List
David
John
Tom
Tom
Why Doesn't Java Compare Every Pair?
Many people think sorting does:
John vs Tom
John vs Tom
John vs David
Tom vs Tom
Tom vs David
Tom vs David
like a nested loop.
That would be: O(n²)
Java's TimSort is much smarter.
It attempts to discover ordered runs and merge them efficiently.
Therefore:
The compare sequence is implementation detail.
Very Important Interview Point
You should NEVER write logic like:
Comparator<Employee> comparator =
new Comparator<>() {
int count = 0;
@Override
public int compare(Employee e1,
Employee e2) {
count++;
if (count == 3) {
return 0;
}
return e1.name()
.compareTo(e2.name());
}
};
because Java may call:
compare()
The comparator must always return the same result for the same inputs.
If You Want to See Exactly What Is Being Compared
Add more details:
@Override
public int compare(Employee o1, Employee o2) {
int result =
o1.name().compareTo(o2.name());
System.out.println(
o1.name()
+ " vs "
+ o2.name()
+ " => "
+ result
);
return result;
}
Output:
Tom vs John => 10
Tom vs Tom => 0
David vs Tom => -16
David vs Tom => -16
David vs John => -6
Now you can see why Java is moving elements around.
Senior Developer Takeaway
When using:
Collections.sort(...)
or
List.sort(...)
you should think:
I provide the comparison rule.
Java decides:
- which elements to compare
- how often to compare
- in what order to compare
The sequence of compare() calls is not deterministic API behavior and may even change between Java versions because it depends on the underlying sorting implementation (currently TimSort).
---------------------------------------------------------------

Top comments (0)