Comparable vs Comparator
Comparable and Comparator are used in Java to define sorting logic for objects. They answer two different questions:
- Comparable: How should this object be compared to another of the same type?
- Comparator: How should two objects be compared using an external rule?
This is a very high-frequency interview topic.
What Is Comparable?
Comparable is an interface that defines natural ordering of objects.
public interface Comparable<T> {
int compareTo(T o);
}
Key Points
- Implemented inside the class
- Defines default / natural order
- Affects TreeSet, TreeMap, Collections.sort()
- Only one sorting logic per class
Comparable Example
class Student implements Comparable<Student> {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public int compareTo(Student s) {
return this.id - s.id; // sort by id
}
}
List<Student> list = new ArrayList<>();
Collections.sort(list);
✔ Sorting happens automatically
✔ Uses compareTo()
What Is Comparator?
Comparator is a separate object that defines custom sorting logic.
public interface Comparator<T> {
int compare(T o1, T o2);
}
Key Points
- Implemented outside the class
- Allows multiple sorting strategies
- Used when class cannot be modified
- Preferred for flexibility
Comparator Example
class NameComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
return s1.name.compareTo(s2.name);
}
}
Collections.sort(list, new NameComparator());
✔ Sorts by name
✔ Class remains unchanged
Comparator Using Lambda (Java 8+)
Collections.sort(list, (s1, s2) -> s1.name.compareTo(s2.name));
✔ Clean
✔ Concise
✔ Modern approach
Sorting with TreeSet / TreeMap
Using Comparable
TreeSet<Student> set = new TreeSet<>();
✔ Uses compareTo()
Using Comparator
TreeSet<Student> set =
new TreeSet<>((a, b) -> a.name.compareTo(b.name));
✔ Uses custom logic
Comparable vs Comparator (Interview Favorite Table)
| Aspect | Comparable | Comparator |
|---|---|---|
| Package | java.lang | java.util |
| Method | compareTo() | compare() |
| Sorting logic | Inside class | Outside class |
| Number of strategies | One | Multiple |
| Class modification | Required | Not required |
| Flexibility | Less | More |
Return Values of compare / compareTo (Important)
| Return Value | Meaning |
|---|---|
| 0 | Objects equal |
| < 0 | First object smaller |
| > 0 | First object greater |
Real-World Analogy
Comparable
Student knows how to compare itself with another student (by roll number)
Comparator
External examiner compares students by name, marks, or age
Common Beginner Mistakes
- Confusing equals() with compareTo()
- Returning wrong comparison values
- Not handling null values
- Using subtraction for large numbers (overflow risk)
- Forgetting consistency with equals()
Best Practices
- Use Comparable for natural/default order
- Use Comparator for alternate sorting
- Prefer Comparator.comparing() (Java 8+)
- Ensure comparison logic is consistent with equals()
- Avoid subtraction for comparison (use Integer.compare())
return Integer.compare(this.id, s.id);
Interview-Ready Answers
Short Answer
Comparable is used for natural ordering, while Comparator is used for custom ordering.
Detailed Answer
In Java, Comparable defines a class’s natural ordering using the compareTo() method, whereas Comparator defines external sorting logic using the compare() method. Comparable allows only one sorting order, while Comparator supports multiple sorting strategies and greater flexibility.
Key Takeaway
Comparable = natural order (inside class). Comparator = custom order (outside class). Use Comparable for default sorting and Comparator for flexible, multiple sorting rules.
Extended Examples
1. Comparable – Natural Ordering (Single Logic)
class Student implements Comparable<Student> {
int id;
Student(int id) {
this.id = id;
}
public int compareTo(Student s) {
return this.id - s.id;
}
public String toString() {
return "" + id;
}
}
import java.util.*;
class Demo {
public static void main(String[] args) {
List<Student> list = Arrays.asList(
new Student(3),
new Student(1),
new Student(2)
);
Collections.sort(list);
System.out.println(list);
}
}
Output
[1, 2, 3]
2. Comparable Affects Default Sorting
import java.util.*;
class Demo {
public static void main(String[] args) {
TreeSet<Student> set = new TreeSet<>();
set.add(new Student(2));
set.add(new Student(1));
System.out.println(set);
}
}
Explanation
- TreeSet uses compareTo()
- Mandatory for natural ordering
3. Comparator – External Sorting Logic
import java.util.*;
class Demo {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(3, 1, 2);
Collections.sort(list, (a, b) -> b - a); // descending
System.out.println(list);
}
}
Output
[3, 2, 1]
4. Sorting Custom Object Using Comparator
class Employee {
int salary;
Employee(int salary) {
this.salary = salary;
}
public String toString() {
return "" + salary;
}
}
import java.util.*;
class Demo {
public static void main(String[] args) {
List<Employee> list = Arrays.asList(
new Employee(5000),
new Employee(3000)
);
Collections.sort(list, (a, b) -> a.salary - b.salary);
System.out.println(list);
}
}
Output
[3000, 5000]
5. Multiple Sorting Logic Using Comparator
import java.util.*;
class Demo {
public static void main(String[] args) {
List<Employee> list = Arrays.asList(
new Employee(5000),
new Employee(3000)
);
Collections.sort(list, (a, b) -> b.salary - a.salary);
System.out.println(list);
}
}
Explanation
- Multiple sort strategies possible
- Not possible with Comparable alone
6. Comparator with TreeSet
import java.util.*;
class Demo {
public static void main(String[] args) {
TreeSet<Integer> set =
new TreeSet<>((a, b) -> b - a);
set.add(1);
set.add(3);
set.add(2);
System.out.println(set);
}
}
Output
[3, 2, 1]
7. Comparable vs Comparator – Method Location
// Comparable → inside class
public int compareTo(T o)
// Comparator → separate class / lambda
public int compare(T o1, T o2)
8. Comparator Using Separate Class
import java.util.*;
class SalaryComparator implements Comparator<Employee> {
public int compare(Employee a, Employee b) {
return a.salary - b.salary;
}
}
class Demo {
public static void main(String[] args) {
List<Employee> list = Arrays.asList(
new Employee(4000),
new Employee(2000)
);
Collections.sort(list, new SalaryComparator());
System.out.println(list);
}
}
9. Comparable Limitation – Only One Sorting Rule
class User implements Comparable<User> {
int age;
User(int age) {
this.age = age;
}
public int compareTo(User u) {
return this.age - u.age;
}
}
Explanation
Cannot sort by name without Comparator.
10. Comparator with Method Reference
import java.util.*;
class Demo {
public static void main(String[] args) {
List<String> list = Arrays.asList("Java", "API", "Test");
list.sort(Comparator.comparingInt(String::length));
System.out.println(list);
}
}
Output
[API, Test, Java]
11. Comparator.thenComparing()
class Person {
int age;
String name;
Person(int age, String name) {
this.age = age;
this.name = name;
}
public String toString() {
return age + "-" + name;
}
}
import java.util.*;
class Demo {
public static void main(String[] args) {
List<Person> list = Arrays.asList(
new Person(30, "B"),
new Person(30, "A"),
new Person(20, "C")
);
list.sort(Comparator
.comparingInt((Person p) -> p.age)
.thenComparing(p -> p.name));
System.out.println(list);
}
}
Output
[20-C, 30-A, 30-B]
12. compareTo() Returning 0 Means Duplicate (TreeSet Trap)
class User implements Comparable<User> {
int id;
User(int id) { this.id = id; }
public int compareTo(User u) {
return this.id - u.id;
}
}
import java.util.*;
class Demo {
public static void main(String[] args) {
TreeSet<User> set = new TreeSet<>();
set.add(new User(1));
set.add(new User(1));
System.out.println(set.size());
}
}
Output
1
13. Comparator Allows Sorting Without Modifying Class
import java.util.*;
class Demo {
public static void main(String[] args) {
List<String> list = Arrays.asList("B", "A");
Collections.sort(list, Comparator.naturalOrder());
System.out.println(list);
}
}
14. Sorting Map Keys Using Comparator
import java.util.*;
class Demo {
public static void main(String[] args) {
TreeMap<Integer, String> map =
new TreeMap<>(Comparator.reverseOrder());
map.put(1, "A");
map.put(2, "B");
System.out.println(map);
}
}
Output
{2=B, 1=A}
15. Comparable vs Comparator – Compilation Rule
// TreeSet without Comparator requires Comparable
TreeSet<Student> set = new TreeSet<>();
// Otherwise
ClassCastException
16. Comparator Using Anonymous Class (Old Style)
Collections.sort(list, new Comparator<Employee>() {
public int compare(Employee a, Employee b) {
return a.salary - b.salary;
}
});
17. Null Handling with Comparator
import java.util.*;
class Demo {
public static void main(String[] args) {
List<String> list = Arrays.asList("A", null, "B");
list.sort(Comparator.nullsLast(String::compareTo));
System.out.println(list);
}
}
Output
[A, B, null]
18. Performance Concept
- Comparable → faster (built-in)
- Comparator → flexible (external)
19. Real Interview Comparison
- Comparable → natural ordering
- Comparator → custom ordering
20. Interview Summary – Comparable vs Comparator
Comparable<T> // compareTo()
Comparator<T> // compare()
Key Points
- Comparable → inside class, one logic
- Comparator → outside class, multiple logics
- TreeSet / TreeMap rely on comparison
- Returning 0 means duplicate
- Comparator is more flexible