Custom Comparator Examples
Java coding interview problem for Collections: Custom Comparator Examples.
Sorting objects is one of the most common Java interview topics.
Java provides two ways to define object ordering:
Comparable
vs
Comparator
While Comparable defines the natural ordering inside a class, Comparator allows us to create custom sorting rules externally.
What is Comparator in Java?
Comparator is a functional interface used to define custom sorting logic.
Package:
java.util.Comparator
Interface:
public interface Comparator<T> {
int compare(T o1, T o2);
}
The compare() method decides:
Which object should come first?
Understanding compare() Method
Syntax:
compare(object1, object2)
Returns:
| Return Value | Meaning |
|---|---|
| Negative | object1 comes before object2 |
| Zero | Both are equal |
| Positive | object1 comes after object2 |
Example
Employees:
John salary=90000
Alice salary=120000
Comparison:
compare(John,Alice)
Result:
Negative value
because:
90000 < 120000
Comparator Flow
Objects
|
Comparator
|
compare()
|
Sorting Algorithm
|
Sorted Result
Comparable vs Comparator
| Feature | Comparable | Comparator |
|---|---|---|
| Package | java.lang | java.util |
| Method | compareTo() | compare() |
| Location | Inside class | Separate class |
| Sorting Rules | Single | Multiple |
| Modifies Class | Yes | No |
| Use Case | Natural ordering | Custom ordering |
Why Custom Comparators Are Needed?
Real applications require different sorting rules.
Example Employee:
id
name
department
salary
experience
Possible sorting:
By Salary
Highest salary first
By Name
Alphabetical order
By Experience
Most experienced first
One Employee class:
Multiple sorting strategies
This is where Comparator is useful.
Real-World Applications
Employee Management
Sort employees by:
- Salary
- Experience
- Joining date
E-Commerce
Sort products by:
- Price
- Rating
- Popularity
Banking
Sort customers by:
- Balance
- Transaction count
- Credit score
Reporting Systems
Generate reports sorted by:
- Date
- Amount
- Priority
Employee Example
We will use:
Employee
|
|-- id
|
|-- name
|
|-- department
|
|-- salary
Employee Class
class Employee {
private int id;
private String name;
private String department;
private double salary;
public Employee(
int id,
String name,
String department,
double salary) {
this.id = id;
this.name = name;
this.department = department;
this.salary = salary;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getDepartment() {
return department;
}
public double getSalary() {
return salary;
}
@Override
public String toString() {
return name +
" - " +
department +
" - " +
salary;
}
}
Example 1 — Sort Employees by Salary
Problem:
Sort employees based on salary ascending order.
Input
John 90000
Alice 120000
Bob 75000
Expected Output
Bob 75000
John 90000
Alice 120000
Creating Salary Comparator
import java.util.Comparator;
class SalaryComparator
implements Comparator<Employee> {
@Override
public int compare(
Employee e1,
Employee e2) {
return Double.compare(
e1.getSalary(),
e2.getSalary()
);
}
}
Using Comparator
import java.util.*;
public class ComparatorExample {
public static void main(String[] args) {
List<Employee> employees =
new ArrayList<>();
employees.add(
new Employee(
101,
"John",
"IT",
90000));
employees.add(
new Employee(
102,
"Alice",
"HR",
120000));
employees.add(
new Employee(
103,
"Bob",
"Finance",
75000));
Collections.sort(
employees,
new SalaryComparator()
);
System.out.println(
employees
);
}
}
Output
Bob - Finance - 75000
John - IT - 90000
Alice - HR - 120000
Step-by-Step Explanation
Before sorting:
90000
120000
75000
Comparator compares:
John vs Alice
90000 < 120000
John stays first.
Compare:
Alice vs Bob
120000 > 75000
Bob moves before Alice.
Final:
75000
90000
120000
Example 1 Using Lambda Expression
Java 8 makes Comparator shorter.
Instead of creating class:
SalaryComparator
use:
employees.sort(
(e1,e2) ->
Double.compare(
e1.getSalary(),
e2.getSalary()
)
);
Example 1 Using Method Reference
Even cleaner:
employees.sort(
Comparator.comparing(
Employee::getSalary
)
);
Descending Salary Sort
Requirement:
Highest salary first
Using:
employees.sort(
Comparator
.comparing(
Employee::getSalary
)
.reversed()
);
Output:
Alice 120000
John 90000
Bob 75000
Example 2 — Sort Employees by Name
Problem:
Sort alphabetically.
Input:
John
Alice
Bob
Output:
Alice
Bob
John
Name Comparator
Comparator<Employee> nameComparator =
Comparator.comparing(
Employee::getName
);
Usage:
employees.sort(
nameComparator
);
String Comparison Internals
For:
String.compareTo()
Java compares:
Unicode values
Example:
Alice < Bob < John
Example 3 — Sort Employees by Multiple Fields
Real applications require:
Primary sorting
+
Secondary sorting
Example:
Sort by:
- Department
- Salary descending
Input:
John IT 90000
Bob IT 120000
Alice HR 80000
Expected:
Alice HR 80000
Bob IT 120000
John IT 90000
Comparator Chaining
Comparator<Employee> comparator =
Comparator.comparing(
Employee::getDepartment
)
.thenComparing(
Employee::getSalary
)
.reversed();
Better Version
Comparator<Employee> comparator =
Comparator.comparing(
Employee::getDepartment
)
.thenComparing(
Comparator.comparing(
Employee::getSalary
)
.reversed()
);
Sorting Flow with Multiple Fields
Department
↓
Same Department?
↓
Compare Salary
↓
Final Order
Complexity Analysis
Java sorting uses:
TimSort
for objects.
Time:
O(n log n)
Space:
O(n)
Advantages of Comparator
- Multiple sorting strategies.
- No class modification required.
- Works with lambda expressions.
- Supports chaining.
- Cleaner enterprise code.
Drawbacks
- More code for complex rules.
- Need comparison understanding.
- Incorrect comparison can produce wrong ordering.
Example 4 — Sort Products by Price
Custom comparators are not limited to employees.
Any object can be sorted by defining comparison rules.
Product Class
class Product {
private int id;
private String name;
private double price;
public Product(
int id,
String name,
double price) {
this.id = id;
this.name = name;
this.price = price;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
@Override
public String toString() {
return name +
" : " +
price;
}
}
Sort Products by Price Ascending
Example:
Input:
Laptop 1200
Phone 800
Tablet 500
Output:
Tablet 500
Phone 800
Laptop 1200
Comparator
Comparator<Product> priceComparator =
Comparator.comparing(
Product::getPrice
);
Sort Products
products.sort(
priceComparator
);
Sort Products by Highest Price
products.sort(
Comparator
.comparing(
Product::getPrice
)
.reversed()
);
Example 5 — Sort Students by Marks
Student sorting is a common interview example.
Student Class
class Student {
private int id;
private String name;
private int marks;
public Student(
int id,
String name,
int marks) {
this.id = id;
this.name = name;
this.marks = marks;
}
public int getMarks(){
return marks;
}
public String getName(){
return name;
}
}
Sort By Marks Descending
students.sort(
Comparator
.comparing(
Student::getMarks
)
.reversed()
);
Output
John 95
Alice 90
Bob 80
Sort Students by Marks Then Name
Requirement:
Higher marks first
If marks same:
Sort by name
Example:
Input:
John 90
Alice 90
Bob 80
Output:
Alice 90
John 90
Bob 80
Comparator Chaining
students.sort(
Comparator
.comparing(
Student::getMarks
)
.reversed()
.thenComparing(
Student::getName
)
);
Example 6 — Sort Transactions by Date
Enterprise applications frequently sort transactions.
Transaction Object
class Transaction {
private int id;
private LocalDate date;
private double amount;
public LocalDate getDate(){
return date;
}
public double getAmount(){
return amount;
}
}
Sort Transactions By Date
transactions.sort(
Comparator.comparing(
Transaction::getDate
)
);
Latest Transaction First
transactions.sort(
Comparator
.comparing(
Transaction::getDate
)
.reversed()
);
Sort By Amount Then Date
Example:
Highest amount first
Same amount:
Latest date first
Code:
transactions.sort(
Comparator
.comparing(
Transaction::getAmount
)
.reversed()
.thenComparing(
Comparator.comparing(
Transaction::getDate
)
.reversed()
)
);
Example 7 — Sort Strings by Length
Comparator can also sort primitive wrapper objects.
Input:
Java
Spring
AI
Sort by length:
Output:
AI
Java
Spring
Java Program
List<String> names =
Arrays.asList(
"Java",
"Spring",
"AI"
);
names.sort(
Comparator.comparing(
String::length
)
);
Descending Length
names.sort(
Comparator
.comparing(
String::length
)
.reversed()
);
Example 8 — Sort Map Entries by Value
A common HashMap interview question:
Input:
Java=5
Spring=3
Boot=1
Sort by value:
Boot=1
Spring=3
Java=5
Comparator With Map.Entry
List<Map.Entry<String,Integer>> entries =
new ArrayList<>(
map.entrySet()
);
entries.sort(
Map.Entry
.comparingByValue()
);
Descending Value Sort
entries.sort(
Map.Entry
.comparingByValue()
.reversed()
);
Example 9 — PriorityQueue With Custom Comparator
PriorityQueue uses Comparator to define priority.
Example:
Process highest salary employee first.
Employee Priority Queue
PriorityQueue<Employee> queue =
new PriorityQueue<>(
Comparator
.comparing(
Employee::getSalary
)
.reversed()
);
Adding Employees
queue.add(
new Employee(
101,
"John",
"IT",
90000
)
);
queue.add(
new Employee(
102,
"Alice",
"HR",
120000
)
);
Remove:
queue.poll();
Returns:
Alice 120000
because she has highest priority.
Example 10 — Stream API Sorting
Modern Java applications commonly use Streams.
Sort Employees Using Stream
List<Employee> sorted =
employees.stream()
.sorted(
Comparator.comparing(
Employee::getSalary
)
)
.toList();
Descending Stream Sort
List<Employee> sorted =
employees.stream()
.sorted(
Comparator
.comparing(
Employee::getSalary
)
.reversed()
)
.toList();
Comparator Chaining
Comparator chaining allows multiple sorting rules.
Example:
Department
↓
Salary
↓
Name
Code:
employees.sort(
Comparator
.comparing(
Employee::getDepartment
)
.thenComparing(
Employee::getSalary
)
.thenComparing(
Employee::getName
)
);
thenComparing()
Used when the first comparison returns:
0
meaning:
Objects are equal
Example:
John IT 90000
Bob IT 90000
Salary comparison:
Equal
Then compare:
Name
reversed()
Reverses sorting order.
Ascending:
Comparator.comparing(
Employee::getSalary
)
Descending:
Comparator.comparing(
Employee::getSalary
)
.reversed()
nullsFirst() and nullsLast()
Real applications may contain null values.
Example:
John 90000
Alice null salary
Null Values Last
Comparator<Employee> comparator =
Comparator.comparing(
Employee::getSalary,
Comparator.nullsLast(
Double::compare
)
);
Output:
John 90000
Alice null
Null Values First
Comparator.comparing(
Employee::getSalary,
Comparator.nullsFirst(
Double::compare
)
);
Custom Comparator for Nested Objects
Example:
Employee has:
Address
|
City
Sort employees by city:
Comparator<Employee> cityComparator =
Comparator.comparing(
employee ->
employee.getAddress()
.getCity()
);
Comparable vs Comparator Deep Comparison
| Feature | Comparable | Comparator |
|---|---|---|
| Defined | Inside class | Outside class |
| Method | compareTo() | compare() |
| Multiple rules | Difficult | Easy |
| Lambda support | No direct | Yes |
| Best for | Natural order | Business sorting |
Common Interview Mistakes
Mistake 1
Incorrect comparison return.
Wrong:
return 1;
for every case.
Mistake 2
Using subtraction.
Wrong:
e1.salary - e2.salary
Correct:
Double.compare(
e1.salary,
e2.salary
);
Mistake 3
Ignoring null handling.
Production data can contain null values.
Mistake 4
Creating many comparator classes unnecessarily.
Java 8 provides:
- Lambda
- Method references
- Comparator chaining
Edge Cases
| Case | Handling |
|---|---|
| Empty list | Return empty |
| Single object | Already sorted |
| Same values | Use secondary comparison |
| Null values | Use nullsFirst/nullsLast |
| Large collections | Avoid unnecessary sorting |
Interview Follow-up Questions
Q1. Difference between Comparable and Comparator?
Q2. How does Comparator work internally?
Q3. Sort employees by multiple fields.
Q4. Sort HashMap by value.
Q5. Use Comparator with PriorityQueue.
Q6. Difference between comparing() and comparingInt().
Q7. How to handle null values?
Related Java Collection Problems
- Sort Employees by Salary
- Sort Map by Value
- Group Employees by Department
- Find Highest Salary Employee
- Remove Duplicate Objects
- Find Duplicate Elements Using Set
Key Takeaways
Custom Comparator follows:
Object List
↓
Define Comparison Rule
↓
Comparator
↓
Sorted Result
Recommended approach:
Simple sorting
Use:
Comparator.comparing()
Multiple conditions
Use:
thenComparing()
Reverse order
Use:
reversed()
Stream sorting
Use:
stream()
.sorted()
Complexity:
Sorting:
O(n log n)
Space:
O(n)
Frequently Asked Interview Questions
Q1. Why use Comparator?
To define custom sorting rules without modifying the class.
Q2. Can one class have multiple Comparators?
Yes.
Example:
SalaryComparator
NameComparator
DateComparator
Q3. What does compare() return?
Negative, zero, or positive value.
Q4. Which sorting algorithm does Java use?
For objects:
TimSort
Interview Tip
When asked:
"Create a custom comparator in Java."
Explain:
- Identify sorting criteria.
- Implement Comparator or use lambda.
- Use
Comparator.comparing(). - Add secondary rules using
thenComparing(). - Handle null values for production systems.
For senior Java interviews, discuss:
- Comparator contract.
- Stable sorting.
- Lambda expressions.
- Stream sorting.
- PriorityQueue usage.
- Performance considerations.
This demonstrates strong understanding of Java Collections, functional programming, and enterprise-level sorting design.