Java Comparator & Comparable
java Comparator Comparable compareTo compare Collections.sort TimSort thenComparing reversed
Learning Objectives
After completing this lesson, you will be able to:
- Understand why sorting is important in Java
- Learn the difference between Comparable and Comparator
- Implement natural ordering using Comparable
- Implement custom sorting using Comparator
- Sort objects using Collections and Streams
- Use Lambda Expressions with Comparator
- Learn Comparator utility methods
- Understand sorting internals
- Apply sorting in Spring Boot applications
- Follow enterprise best practices
- Prepare for Java interview questions
Introduction
Sorting is one of the most common operations in enterprise applications.
Examples:
- Sort employees by salary
- Sort products by price
- Sort students by marks
- Sort orders by date
- Sort customers by name
- Sort transactions by amount
- Sort logs by timestamp
- Sort flights by departure time
Java provides two mechanisms for sorting objects:
- Comparable
- Comparator
Both are extensively used in:
- Collections Framework
- Streams API
- Spring Boot
- Hibernate
- JavaFX
- Enterprise Applications
Why Comparable & Comparator?
Suppose we have an Employee class.
public class Employee {private int id;private String name;private double salary;}
Now suppose we have:
List<Employee> employees = ...
How should Java sort these employees?
- By ID?
- By Name?
- By Salary?
- By Joining Date?
Java needs a way to know the sorting logic.
This is where Comparable and Comparator come into the picture.
Comparable vs Comparator
| Comparable | Comparator |
|---|---|
| Package: java.lang | Package: java.util |
| Natural Ordering | Custom Ordering |
| Inside the class | Outside the class |
| One sorting strategy | Multiple sorting strategies |
| Implements compareTo() | Implements compare() |
Sorting Architecture
List<Employee>│┌─────────────┴─────────────┐│ │Comparable Comparator│ │Natural Sorting Custom Sorting
What is Comparable?
Comparable defines the natural ordering of objects.
Interface
public interface Comparable<T>{int compareTo(T object);}
Implementing Comparable
public class Employeeimplements Comparable<Employee>{private int id;private String name;@Overridepublic int compareTo(Employee other){return this.id - other.id;}}
Sorting using Comparable
List<Employee> employees =new ArrayList<>();Collections.sort(employees);
Java automatically calls:
compareTo()
Example Output
Before Sorting
102 Rahul101 Amit103 John
After Sorting
101 Amit102 Rahul103 John
compareTo() Return Values
this.compareTo(other)
| Return Value | Meaning |
|---|---|
| Negative | Current object is smaller |
| Zero | Equal |
| Positive | Current object is greater |
Example
return this.salary.compareTo(other.salary);
What is Comparator?
Comparator allows multiple custom sorting strategies.
Interface
public interface Comparator<T>{int compare(T o1, T o2);}
Sorting by Name
public class NameComparatorimplements Comparator<Employee>{@Overridepublic int compare(Employee e1,Employee e2){return e1.getName().compareTo(e2.getName());}}
Usage
Collections.sort(employees,new NameComparator());
Sorting by Salary
public class SalaryComparatorimplements Comparator<Employee>{@Overridepublic int compare(Employee e1,Employee e2){return Double.compare(e1.getSalary(),e2.getSalary());}}
Comparator using Lambda
Java 8 simplified sorting.
employees.sort((a,b) ->Double.compare(a.getSalary(),b.getSalary()));
Comparator using Method Reference
employees.sort(Comparator.comparing(Employee::getName));
Reverse Sorting
employees.sort(Comparator.comparing(Employee::getSalary).reversed());
Multiple Field Sorting
Sort by Department, then Salary.
employees.sort(Comparator.comparing(Employee::getDepartment).thenComparing(Employee::getSalary));
Comparator Utility Methods
Java provides many helper methods.
comparing()
Comparator.comparing(Employee::getName);
reversed()
Comparator.comparing(Employee::getSalary).reversed();
thenComparing()
Comparator.comparing(Employee::getDepartment).thenComparing(Employee::getName);
nullsFirst()
Comparator.nullsFirst(Comparator.naturalOrder());
nullsLast()
Comparator.nullsLast(Comparator.naturalOrder());
Sorting using Streams
employees.stream().sorted(Comparator.comparing(Employee::getSalary)).forEach(System.out::println);
Comparable vs Comparator Example
Comparable
Employee↓Sort by Employee ID
Comparator
Employee↓Sort by Salary↓Sort by Name↓Sort by Department↓Sort by Experience
Internal Working
Collections.sort()↓TimSort Algorithm↓compareTo()/compare()↓Sorted Collection
Java uses TimSort, a hybrid sorting algorithm derived from Merge Sort and Insertion Sort.
Complexity
| Case | Complexity |
|---|---|
| Best | O(n) |
| Average | O(n log n) |
| Worst | O(n log n) |
Real-World Example: Employee Management
employees.sort(Comparator.comparing(Employee::getDepartment).thenComparing(Employee::getSalary).reversed());
Real-World Example: Product Sorting
products.sort(Comparator.comparing(Product::getPrice));
Real-World Example: Student Ranking
students.sort(Comparator.comparing(Student::getMarks).reversed());
Spring Boot Example
Repository
List<Employee> employees =repository.findAll();
Sorting
employees.sort(Comparator.comparing(Employee::getJoiningDate));
Best Practices
Use Comparable for Natural Ordering
Example
- Employee ID
- Roll Number
- Account Number
Use Comparator for Business Rules
Example
- Salary
- Department
- Name
- Joining Date
Prefer Comparator.comparing()
Cleaner than manually implementing compare().
Use Lambda Expressions
Prefer concise lambda expressions over anonymous classes.
Avoid Subtraction for Integer Comparison
Instead of
return a - b;
Use
return Integer.compare(a,b);
This avoids integer overflow.
Chain Comparisons
Use thenComparing() instead of nested if-else blocks.
Common Mistakes
Returning Incorrect Values
Wrong
return 1;
Always implement consistent comparison logic.
Using == for Strings
Wrong
name1 == name2
Correct
name1.compareTo(name2)
or
Objects.equals(name1, name2)
depending on the requirement.
Ignoring Null Values
Use
Comparator.nullsFirst()
or
Comparator.nullsLast()
Violating compareTo() Contract
Ensure comparisons are:
- Consistent
- Symmetric
- Transitive
Multiple Comparable Implementations
A class can implement only one natural ordering. Use Comparators for additional sorting strategies.
Hands-on Exercise
Create a Java program that:
- Creates an
Employeeclass implementingComparable. - Sorts employees by ID.
- Creates a
SalaryComparator. - Creates a
NameComparator. - Sorts using Lambda expressions.
- Uses
Comparator.comparing(). - Uses
thenComparing(). - Uses
reversed(). - Uses
nullsFirst(). - Sorts employees using the Streams API.
Summary
Comparable and Comparator provide the foundation for sorting in Java. Comparable defines a class's natural ordering, while Comparator enables multiple custom sorting strategies. Modern Java encourages using Comparator.comparing(), lambda expressions, and method references to build concise, readable, and maintainable sorting logic. These APIs are heavily used in enterprise applications, especially with Collections, Streams, Spring Boot, and data processing.
Key Takeaways
Comparabledefines natural ordering.Comparatordefines custom ordering.compareTo()belongs toComparable.compare()belongs toComparator.- Use
Comparator.comparing()for clean code. - Use
thenComparing()for multi-level sorting. - Use
reversed()for descending order. - Handle null values with
nullsFirst()andnullsLast(). - Streams integrate seamlessly with Comparator.
- Java uses TimSort for sorting object collections.
Professional Interview Questions
1What is the difference between Comparable and Comparator?
Professional Answer
Comparable is used to define the natural ordering of a class and requires implementing the compareTo() method within the class itself. Comparator is used to define external, customizable sorting logic through the compare() method, allowing multiple sorting strategies for the same class without modifying its source code.
Follow-up Questions
- Which package contains Comparable?
- Can a class have multiple Comparator implementations?
- Can a class implement more than one Comparable?
Interview Tip: Comparable → Natural Order, Comparator → Custom Order.
2Why should Integer.compare() or Double.compare() be preferred over subtraction?
Professional Answer
Using subtraction, such as return a - b, can lead to integer overflow when comparing very large or very small values. Methods like Integer.compare(), Long.compare(), and Double.compare() provide safe, readable, and overflow-free comparisons, making them the recommended approach in production code.
Follow-up Questions
- Can subtraction cause incorrect sorting?
- Which compare method should be used for floating-point values?
Interview Tip: Always prefer Integer.compare(), Long.compare(), Double.compare() instead of arithmetic subtraction.
3What sorting algorithm does Collections.sort() use?
Professional Answer
Since Java 7, Collections.sort() and List.sort() use TimSort, a hybrid sorting algorithm derived from Merge Sort and Insertion Sort. TimSort is stable, performs exceptionally well on partially sorted data, and has a worst-case time complexity of O(n log n), making it suitable for enterprise applications.
Follow-up Questions
- Is TimSort a stable sorting algorithm?
- What is its average time complexity?
- Does Arrays.sort() always use TimSort?
Interview Tip: Objects (Collections.sort, List.sort) → TimSort, Primitive arrays (Arrays.sort(int[])) → Dual-Pivot QuickSort.