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:

  1. Department
  2. 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:

  1. Identify sorting criteria.
  2. Implement Comparator or use lambda.
  3. Use Comparator.comparing().
  4. Add secondary rules using thenComparing().
  5. 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.