Comparable and Comparator


Comparable and Comparator Interview with follow-up questions

1. What is the difference between Comparable and Comparator in Java?

Both define how objects are ordered, but differ in where the logic lives and how many orderings you get:

  • Comparable — implemented by the class itself; defines its single natural ordering via int compareTo(T other). Use when there's one obvious default order (e.g. numbers, String, dates).
  • Comparator — a separate object defining custom/alternative orderings via int compare(T a, T b). Use when you need multiple sort orders, can't modify the class, or want order decided at the call site.
class Employee implements Comparable {
    public int compareTo(Employee o) { return Integer.compare(this.id, o.id); } // natural: by id
}
// alternative orders without touching the class:
employees.sort(Comparator.comparing(Employee::name).thenComparing(Employee::salary).reversed());
Comparable Comparator
Package/method java.lang, compareTo(o) java.util, compare(a,b)
Lives in the class itself a separate class/lambda
Orderings one (natural) many
Modifies the class? yes no

The modern detail interviewers reward: you rarely write a Comparator class anymore — use the factory/combinator methods: Comparator.comparing(...), thenComparing(...), reversed(), nullsFirst(...), and comparingInt/Long/Double. All three of compareTo/compare return negative / 0 / positive, and should be consistent with equals for sorted sets/maps. Use Comparable for the one natural order, Comparator for everything else.

↑ Back to top

Follow-up 1

Can you give an example where you would use Comparable?

Sure! Let's say we have a class called Person with attributes name and age. If we want to sort a list of Person objects based on their age, we can make the Person class implement the Comparable interface and override the compareTo() method. Here's an example:

public class Person implements Comparable {
    private String name;
    private int age;

    // Constructor and other methods

    @Override
    public int compareTo(Person otherPerson) {
        return this.age - otherPerson.age;
    }
}

In this example, the compareTo() method compares the ages of two Person objects and returns a negative integer, zero, or a positive integer based on the comparison result. This allows us to use the Collections.sort() method to sort a list of Person objects based on their age.

Follow-up 2

Can you give an example where you would use Comparator?

Certainly! Let's say we have a class called Employee with attributes name and salary. If we want to sort a list of Employee objects based on their salary in descending order, we can create a separate class called SalaryComparator that implements the Comparator interface and override the compare() method. Here's an example:

import java.util.Comparator;

public class SalaryComparator implements Comparator {
    @Override
    public int compare(Employee employee1, Employee employee2) {
        return employee2.getSalary() - employee1.getSalary();
    }
}

In this example, the compare() method compares the salaries of two Employee objects and returns a negative integer, zero, or a positive integer based on the comparison result. We can then use the Collections.sort() method with an instance of the SalaryComparator class to sort a list of Employee objects based on their salary in descending order.

Follow-up 3

What happens if we compare two null values?

If we compare two null values using either Comparable or Comparator, a NullPointerException will be thrown. This is because null is not an instance of any class, and calling a method on a null object reference will result in a NullPointerException. To avoid this, it is important to handle null values separately before performing any comparison.

Follow-up 4

How can we sort in descending order using Comparable or Comparator?

To sort in descending order using Comparable, we can modify the compareTo() method to return the negation of the comparison result. Here's an example:

public class Person implements Comparable {
    private String name;
    private int age;

    // Constructor and other methods

    @Override
    public int compareTo(Person otherPerson) {
        return -(this.age - otherPerson.age);
    }
}

In this example, the negation of the comparison result ensures that the list of Person objects is sorted in descending order based on their age.

To sort in descending order using Comparator, we can modify the compare() method to reverse the order of the comparison result. Here's an example:

import java.util.Comparator;

public class SalaryComparator implements Comparator {
    @Override
    public int compare(Employee employee1, Employee employee2) {
        return employee2.getSalary() - employee1.getSalary();
    }
}

In this example, the order of the comparison result is reversed by subtracting employee1.getSalary() from employee2.getSalary(), ensuring that the list of Employee objects is sorted in descending order based on their salary.

2. How to use Comparator interface in Java?

A Comparator defines a custom ordering as a separate object, used to sort or to seed sorted collections. The modern way is a lambda or the Comparator factory methods — you rarely write a separate class anymore:

List emps = ...;

// Modern, declarative — preferred:
emps.sort(Comparator.comparing(Employee::getName));                 // by name
emps.sort(Comparator.comparingInt(Employee::getAge).reversed());    // by age desc
emps.sort(Comparator.comparing(Employee::getDept)
                    .thenComparing(Employee::getSalary));            // multi-key

// Lambda form:
emps.sort((a, b) -> a.getName().compareTo(b.getName()));

How it's applied: pass the comparator to List.sort(cmp) (preferred over the older Collections.sort(list, cmp)), Arrays.sort(arr, cmp), Stream.sorted(cmp), or a TreeMap/TreeSet/PriorityKey constructor.

Points interviewers reward: prefer the combinatorscomparing, thenComparing (tie-breakers), reversed, comparingInt/Long/Double (avoid autoboxing), and nullsFirst/nullsLast for null-safety — over hand-written compare methods (less error-prone than manual subtraction, which can overflow: use Integer.compare(a,b), not a-b). A Comparator is also a functional interface, so a lambda works anywhere one is expected. This declarative style is exactly what signals current Java fluency.

↑ Back to top

Follow-up 1

What methods does the Comparator interface have?

The Comparator interface in Java has a single method:

  • int compare(T o1, T o2): This method compares two objects of type T and returns an integer value. It should return a negative integer if o1 is less than o2, zero if o1 is equal to o2, and a positive integer if o1 is greater than o2.

Note: The type parameter T represents the type of objects being compared.

Follow-up 2

Can you write a code snippet to demonstrate the use of Comparator?

Sure! Here is an example code snippet that demonstrates the use of Comparator to sort a list of custom objects:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class AgeComparator implements Comparator {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getAge() - s2.getAge();
    }
}

public class Main {
    public static void main(String[] args) {
        ArrayList students = new ArrayList<>();
        students.add(new Student("John", 20));
        students.add(new Student("Alice", 18));
        students.add(new Student("Bob", 22));

        Collections.sort(students, new AgeComparator());

        for (Student student : students) {
            System.out.println(student.getName() + " - " + student.getAge());
        }
    }
}

Follow-up 3

What is the purpose of the compare() method in Comparator?

The compare() method in the Comparator interface is used to define the custom sorting logic. It compares two objects and determines their relative order.

The compare() method should return a negative integer if the first object is less than the second object, zero if they are equal, and a positive integer if the first object is greater than the second object.

By implementing the compare() method, you can customize the sorting behavior of objects in a collection.

Follow-up 4

How can we use Comparator to sort a list of custom objects?

To use Comparator to sort a list of custom objects, you need to follow these steps:

  1. Create a class that implements the Comparator interface and specify the type of objects being compared.
  2. Implement the compare() method in the class to define the custom sorting logic based on the desired property of the objects.
  3. Use the Comparator object to sort the list of custom objects using the Collections.sort() method or the Arrays.sort() method.

Here is an example of using Comparator to sort a list of Student objects based on their age:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class AgeComparator implements Comparator {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getAge() - s2.getAge();
    }
}

public class Main {
    public static void main(String[] args) {
        ArrayList students = new ArrayList<>();
        students.add(new Student("John", 20));
        students.add(new Student("Alice", 18));
        students.add(new Student("Bob", 22));

        Collections.sort(students, new AgeComparator());

        for (Student student : students) {
            System.out.println(student.getName() + " - " + student.getAge());
        }
    }
}

3. How to use Comparable interface in Java?

Implement Comparable in the class itself and override int compareTo(T other) to define its natural ordering. The method returns a negative number, zero, or positive when this is less than, equal to, or greater than the argument:

class Employee implements Comparable {
    int id; String name;
    @Override public int compareTo(Employee o) {
        return Integer.compare(this.id, o.id);   // natural order: by id
    }
}

Once it implements Comparable, objects sort automatically with Collections.sort(list) / list.sort(null), Arrays.sort(arr), Stream.sorted(), and slot into TreeSet/TreeMap without an explicit comparator.

Best-practice points interviewers want:

  • Use Integer.compare(a, b) (or Long/Double.compare) rather than a - b — subtraction can overflow and give wrong results.
  • For multi-field natural order, delegate to comparator combinators: return Comparator.comparing(Employee::getName).thenComparingInt(e -> e.id).compare(this, o);.
  • Keep compareTo consistent with equals (i.e. compareTo == 0 iff equals is true) — otherwise TreeSet/TreeMap behave surprisingly (they use compareTo, not equals, for uniqueness).

So Comparable defines the one inherent order of the type; for additional orderings you pass a Comparator instead.

↑ Back to top

Follow-up 1

What methods does the Comparable interface have?

The Comparable interface in Java has only one method:

int compareTo(T other)

This method compares the current object with another object of the same type and returns a negative integer, zero, or a positive integer based on whether the current object is less than, equal to, or greater than the other object.

Follow-up 2

Can you write a code snippet to demonstrate the use of Comparable?

Sure! Here's an example code snippet that demonstrates the use of Comparable:

import java.util.ArrayList;
import java.util.Collections;

public class Person implements Comparable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public int compareTo(Person other) {
        return this.age - other.age;
    }

    public static void main(String[] args) {
        ArrayList people = new ArrayList<>();
        people.add(new Person("Alice", 25));
        people.add(new Person("Bob", 30));
        people.add(new Person("Charlie", 20));

        Collections.sort(people);

        for (Person person : people) {
            System.out.println(person.getName() + " - " + person.getAge());
        }
    }
}

In this example, the Person class implements the Comparable interface and overrides the compareTo() method to compare people based on their age. The ArrayList of Person objects is then sorted using the Collections.sort() method, which internally uses the compareTo() method to determine the order.

Follow-up 3

What is the purpose of the compareTo() method in Comparable?

The purpose of the compareTo() method in the Comparable interface is to define the natural ordering of objects. By implementing the Comparable interface and overriding the compareTo() method, objects of a class can be compared and sorted based on their natural ordering. The compareTo() method returns a negative integer, zero, or a positive integer based on whether the current object is less than, equal to, or greater than the other object, respectively.

Follow-up 4

How can we use Comparable to sort a list of custom objects?

To use Comparable to sort a list of custom objects, follow these steps:

  1. Implement the Comparable interface in the custom class by adding the implements Comparable clause.
  2. Override the compareTo() method in the custom class to define the comparison logic based on the desired sorting criteria.
  3. Use the Collections.sort() method to sort the list of custom objects.

Here's an example code snippet that demonstrates this:

import java.util.ArrayList;
import java.util.Collections;

public class CustomClass implements Comparable {
    private int value;

    public CustomClass(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    @Override
    public int compareTo(CustomClass other) {
        return this.value - other.value;
    }

    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        list.add(new CustomClass(3));
        list.add(new CustomClass(1));
        list.add(new CustomClass(2));

        Collections.sort(list);

        for (CustomClass obj : list) {
            System.out.println(obj.getValue());
        }
    }
}

In this example, the CustomClass implements the Comparable interface and overrides the compareTo() method to compare objects based on their value property. The list of CustomClass objects is then sorted using the Collections.sort() method, which internally uses the compareTo() method to determine the order.

4. Can we use both Comparable and Comparator in the same class?

Yes — and it's a common, idiomatic pattern. A class can implement Comparable to define its one natural ordering, while Comparators (defined outside, or as static factory fields) provide alternative orderings. They're not mutually exclusive.

class Employee implements Comparable {
    int id; String name; double salary;

    @Override public int compareTo(Employee o) {        // natural order: by id
        return Integer.compare(id, o.id);
    }
    // alternative orderings exposed as reusable comparators:
    public static final Comparator BY_NAME   = Comparator.comparing(e -> e.name);
    public static final Comparator BY_SALARY = Comparator.comparingDouble(e -> e.salary);
}

list.sort(null);                 // uses compareTo (natural: by id)
list.sort(Employee.BY_SALARY);   // uses the comparator (by salary)

The framing interviewers want: use Comparable for the single, default "natural" order that makes sense for the type, and Comparator(s) for any number of additional orders chosen per use case — without modifying or duplicating the class. Sorting APIs accept either: sort(null)/Collections.sort(list) use the natural order, while sort(comparator) overrides it. So a well-designed class often defines a natural order via compareTo and also offers a few named comparators for other sort keys.

↑ Back to top

Follow-up 1

If yes, how can we achieve that?

To achieve that, we need to implement the Comparable interface and override the compareTo() method to define the natural ordering of the objects in the class. Additionally, we can also create a separate class that implements the Comparator interface and override the compare() method to define a custom ordering for the objects.

Follow-up 2

If no, why not?

N/A

Follow-up 3

What are the advantages and disadvantages of using both in the same class?

Advantages:

  • Using both Comparable and Comparator allows for more flexibility in sorting and comparing objects.
  • It allows us to define both the natural ordering and custom ordering for the objects in the same class.

Disadvantages:

  • It can make the code more complex and harder to understand.
  • It may lead to confusion and inconsistency if the natural ordering and custom ordering are not properly defined or used.

Follow-up 4

Can you write a code snippet to demonstrate the use of both in the same class?

Sure, here's an example:

import java.util.Comparator;

public class Person implements Comparable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public int compareTo(Person other) {
        return this.name.compareTo(other.getName());
    }

    public static class AgeComparator implements Comparator {
        @Override
        public int compare(Person p1, Person p2) {
            return Integer.compare(p1.getAge(), p2.getAge());
        }
    }

    public static void main(String[] args) {
        List persons = new ArrayList<>();
        persons.add(new Person("John", 25));
        persons.add(new Person("Alice", 30));
        persons.add(new Person("Bob", 20));

        Collections.sort(persons); // Sort by name (natural ordering)
        System.out.println("Sorted by name: " + persons);

        Collections.sort(persons, new AgeComparator()); // Sort by age (custom ordering)
        System.out.println("Sorted by age: " + persons);
    }
}

In this example, the Person class implements the Comparable interface to define the natural ordering based on the person's name. It also has a nested class AgeComparator that implements the Comparator interface to define a custom ordering based on the person's age. The main method demonstrates the usage of both Comparable and Comparator to sort the list of Person objects by name and age respectively.

5. What is natural ordering in Java?

Natural ordering is the default, intrinsic order of a type, defined by its Comparable.compareTo() implementation. It's the order used when you sort without supplying a Comparator (e.g. Collections.sort(list), list.sort(null), TreeSet/TreeMap).

Examples built into the JDK:

  • Numbers (Integer, Double, …) → ascending numeric value.
  • Stringlexicographic order based on Unicode code units (note: this is case-sensitive — "Z" sorts before "a").
  • LocalDate/LocalDateTime → chronological.
  • enum → declaration order (by ordinal).
List nums = new ArrayList<>(List.of(3, 1, 2));
Collections.sort(nums);   // [1, 2, 3] — natural ordering

Points interviewers want: a class has natural ordering only if it implements Comparable — otherwise Collections.sort(list) won't compile and a TreeSet will throw ClassCastException; you must pass a Comparator instead. Best practice is to keep natural ordering consistent with equals (compareTo == 0equals), since sorted collections (TreeSet/TreeMap) use compareTo for both ordering and uniqueness — an inconsistency causes elements to be wrongly treated as duplicates. When the natural order isn't what you need, override it with a Comparator.

↑ Back to top

Follow-up 1

How does Comparable interface help in maintaining natural ordering?

The Comparable interface in Java is used to define the natural ordering of objects. By implementing the Comparable interface, a class can specify how its instances should be ordered. The compareTo() method of the Comparable interface is used to compare two objects and determine their relative ordering. This method returns a negative integer, zero, or a positive integer depending on whether the current object is less than, equal to, or greater than the specified object.

Follow-up 2

Can we maintain natural ordering using Comparator?

Yes, we can maintain natural ordering using the Comparator interface in Java. While the Comparable interface is used to define the natural ordering within a class, the Comparator interface is used to define custom ordering for objects that do not implement Comparable or when we want to override the natural ordering. The compare() method of the Comparator interface is used to compare two objects and determine their relative ordering.

Follow-up 3

What is the difference between natural ordering and custom ordering?

The difference between natural ordering and custom ordering is as follows:

  • Natural ordering is the default ordering of objects based on their inherent characteristics, while custom ordering is a user-defined ordering based on specific criteria.
  • Natural ordering is defined by implementing the Comparable interface within the class of the objects being ordered, while custom ordering is defined by implementing the Comparator interface or using lambda expressions.
  • Natural ordering is used when the default ordering makes sense, while custom ordering is used when we want to override the default ordering or define a different ordering criteria.

Follow-up 4

Can you give an example of natural ordering and custom ordering?

Sure! Here's an example of natural ordering and custom ordering:

Natural Ordering:

import java.util.ArrayList;
import java.util.Collections;

public class NaturalOrderingExample {
    public static void main(String[] args) {
        ArrayList numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(8);
        numbers.add(1);
        numbers.add(3);

        Collections.sort(numbers);

        System.out.println(numbers); // Output: [1, 2, 3, 5, 8]
    }
}

Custom Ordering:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class CustomOrderingExample {
    public static void main(String[] args) {
        ArrayList names = new ArrayList<>();
        names.add("John");
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");

        Collections.sort(names, new Comparator() {
            @Override
            public int compare(String s1, String s2) {
                return s1.length() - s2.length();
            }
        });

        System.out.println(names); // Output: [Bob, John, Alice, David, Charlie]
    }
}

In the natural ordering example, the numbers are sorted in ascending order based on their numerical values. In the custom ordering example, the names are sorted in ascending order based on their lengths.

Live mock interview

Mock interview: Comparable and Comparator

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.