Do you think IIT Guwahati certified course can help you in your career?
No
Introduction
Comparator interface in Java is a functional interface used to define custom sorting logic for objects. It provides the compare(T o1, T o2) method, allowing developers to control how two objects are compared and ordered. This is especially useful when sorting collections with different comparison criteria.
Java Comparator is a part of the Java Collection Framework. The Java Collection framework is a large collection of classes and interfaces that provide a versatile architecture for storing, organizing, and manipulating groupings of objects. It provides a diverse set of data structures and algorithms for performing actions on collections of items, such as searching, sorting, insertion, manipulation, and deletion.
What is the Java Comparator Interface?
The Comparator interface in the Java Collections Framework provides a way to order objects of user-defined classes based on specific criteria. It allows for custom comparison logic, enabling sorting operations on objects that may belong to different classes.
The following are some use cases of the Java Comparator Interface:-
Customized Sorting: The primary advantage of the Comparator interface is that it allows you to define custom sorting orders for objects that may not have a natural ordering.
Separate Sorting Logic: With the Comparator interface, you can separate the sorting logic from the class definition itself. This makes your code cleaner and more maintainable.
Multiple Sorting Orders: You can define multiple Comparator implementations for the same class, each representing a different sorting order. This allows you to sort the same set of objects in various ways without modifying the original class.
Now, let us take a look at the methods of the Java comparator interface.
Java Comparator Interface example
Here is an example of how to use the Java Comparator Interface:
import java.util.Comparator;
class LengthComparator implements Comparator<String> {
// Compare strings based on their length
public int compare(String str1, String str2) {
return Integer.compare(str1.length(), str2.length());
}
// Optional: Overriding equals() to check reference equality
@Override
public boolean equals(Object obj) {
return this == obj;
}
}
class HelloWorld {
public static void main(String[] args) {
LengthComparator comparator = new LengthComparator();
String string1 = "Coding";
String string2 = "Ninjas"; // Use correct quotes
// Compare the lengths of the two strings
int result = comparator.compare(string1, string2);
// Check if the comparator is equal to itself (this will always be true)
boolean isEqual = comparator.equals(comparator);
// Print the results
System.out.println("Comparison result: " + result);
System.out.println("Are Comparators equal? " + isEqual);
}
}
You can also try this code with Online Java Compiler
In this example, we have a custom LengthComparator class that implements the Comparator interface and is used to compare the lengths of String objects. The compare() method compares the lengths of two strings by calling “Integer.compare()”. The equals() method simply determines whether the two comparator instances (this and obj) are equal.
In the main() method, we build a comparator instance of the LengthComparator class. In addition, we make two String objects, string1, and string2. We compare their lengths using the compare() method and save the result in the result variable. Because the length of both strings is equal, therefore the output is 0. The equals() method is then used to determine whether the comparator is equal to itself, and the result is placed in the isEqual variable. Because this and obj are referring to the same memory location, therefore the output is true.
Use Cases for Comparator
The Comparator interface in Java is widely used when you need to sort objects based on different attributes or criteria, especially when natural ordering (defined by Comparable) is not sufficient or appropriate.
Here are some common use cases:
Sorting Custom Objects by Specific Fields When working with custom classes (e.g., Employee, Product), you may want to sort by name, salary, price, or date. With Comparator, you can define multiple sorting strategies without modifying the class itself.
Dynamic Sorting at Runtime Comparator allows you to choose the sorting logic dynamically at runtime, such as sorting ascending or descending based on user input.
Multiple Criteria Sorting You can chain comparators to sort by multiple fields, like sorting students by grade and then by name:
Sorting in Data Structures Comparator is essential in TreeSet, TreeMap, and priority queues to maintain a custom order.
These use cases make the Comparator interface a powerful tool for writing flexible, reusable, and maintainable sorting logic in Java applications.
Methods of Comparator Interface in Java
The Comparator interface contains two methods:
Compare Method
Equals Method
compare() Method
This method compares two objects, obj1, and obj2, and returns an integer value to indicate their relative order.
Syntax
public int compare(Object obj1, Object obj2)
Return Value
The return value follows the following convention:
If obj1 should be placed before obj2, a negative value is returned.
If obj1 should be placed after obj2, a positive value is returned.
If obj1 and obj2 are considered equal in terms of ordering, zero is returned.
equals() Method
This method checks whether the invoking Comparator is equal to the specified element. It returns true if the element is also a Comparator object and both Comparators impose the same ordering.
By implementing the Comparator interface and defining the compare() method, we can specify how objects should be ordered based on specific attributes or data members. For example, we can create a Comparator for a Student class that compares students based on their GPA or age.
Syntax
public boolean equals(Object obj)
Return Value
true: If equals() returns true, it means that the two Comparator instances are the same and correspond to the same memory object.
false: If equals() returns false, it indicates that the two Comparator instances are not the same and correspond to different objects in memory.
Alternative Approach: Using Comparator with Lambda Expression
Java 8 introduced lambda expressions, which simplify the use of the Comparator interface by allowing inline definitions without creating separate classes or anonymous inner classes. This makes the code cleaner and more concise.
Example: Sorting a List of Employees by Salary
import java.util.*;
class Employee {
String name;
double salary;
Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String toString() {
return name + " - " + salary;
}
}
public class ComparatorLambdaExample {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("Alice", 70000));
employees.add(new Employee("Bob", 50000));
employees.add(new Employee("Charlie", 60000));
// Using Comparator with Lambda Expression to sort by salary
employees.sort((e1, e2) -> Double.compare(e1.salary, e2.salary));
// Printing sorted list
for (Employee e : employees) {
System.out.println(e);
}
}
}
You can also try this code with Online Java Compiler
The lambda expression (e1, e2) -> Double.compare(e1.salary, e2.salary) acts as a custom comparator.
It compares employees based on their salary in ascending order.
This approach makes the sorting logic short and intuitive, enhancing code readability and maintainability.
Java Comparator Interface to Sort an Object
We can sort objects in Java using custom logic. We can do this by overriding compare method in the Comparator interface.
Java
Java
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; class HelloWorld { public static void main(String[] args) { List<Integer> arr = new ArrayList<>(); arr.add(2); arr.add(0); arr.add(6); arr.add(3); arr.add(9);
// Sort the list in descending order Collections.sort(arr, new Comparator<Integer>() { @Override public int compare(Integer num1, Integer num2) { // Sorting in descending order return num2-num1; } }); System.out.println(arr); } }
You can also try this code with Online Java Compiler
In the example above, we have an array list named arr. After inserting sufficient elements, we override the compare function of the Comparator interface. To implement descending order logic, we return num2-num1. The compare method is supposed to return positive and negative or zero. It will be only positive if the second parameter is greater than the first one, which basically translates to num2 being supposed to place before num1.
Java 8 Comparator Interface
In Java 8, the Comparator interface received significant enhancements to increase its power and flexibility, which makes it more functional and easier to use in modern Java applications. A key feature introduced in Java 8 is the ability to define Comparator instances using lambda expressions, which greatly simplifies the implementation of comparison logic for sorting collections.
A few important points about the Java 8 Comparator interface:
Functional Interface: The Comparator interface is a functional interface, which means it has a single abstract method. In this case, the abstract method is compare(T o1, T o2), which compares two objects and returns an integer value indicating their relative order.
Lambda Expressions: With Java 8, you can use lambda expressions to create Comparator instances inline, without the need for an explicit class implementation. This makes the code more concise and readable.
Methods of Java 8 Comparator Interface
1. Default Methods
Java 8 introduced default methods in interfaces and the Comparator interface benefits from this feature. It provides several default methods that allow you to create Comparators based on various criteria: - reversed(): Returns a Comparator that imposes the reverse ordering of the original Comparator. - thenComparing(Comparator other): Returns a Comparator that first applies the original Comparator, and if the objects are equal, applies the provided Comparator. - thenComparing(Function keyExtractor): Returns a Comparator that first applies the original Comparator, and if the objects are equal, compares them based on the extracted key using the natural ordering. - thenComparingInt(ToIntFunction keyExtractor), thenComparingLong(ToLongFunction keyExtractor), thenComparingDouble(ToDoubleFunction keyExtractor): Similar to thenComparing(Function), but specifically for int, long, and double keys.
2. Static Methods
The Comparator interface also provides static methods for creating Comparators based on different criteria: - comparing(Function keyExtractor): Creates a Comparator based on the extracted key using the natural ordering. - comparing(Function keyExtractor, Comparator keyComparator): Creates a Comparator based on the extracted key using the provided Comparator. - comparingInt(ToIntFunction keyExtractor), comparingLong(ToLongFunction keyExtractor), comparingDouble(ToDoubleFunction keyExtractor): Similar to comparing(Function), but specifically for int, long, and double keys. - naturalOrder(): Returns a Comparator that compares objects using their natural ordering (defined by the compareTo() method). - nullsFirst(Comparator comparator), nullsLast(Comparator comparator): Returns a Comparator that treats null values as either first or last in the ordering, respectively.
class Person { 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 String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
public class ComparatorExample { public static void main(String[] args) { List<Person> people = new ArrayList<>(); people.add(new Person("Alice", 25)); people.add(new Person("Bob", 30)); people.add(new Person("Charlie", 20)); people.add(new Person("David", 35));
// Sort by name using lambda expression people.sort(Comparator.comparing(Person::getName)); System.out.println("Sorted by name: " + people);
// Sort by age using lambda expression people.sort(Comparator.comparingInt(Person::getAge)); System.out.println("Sorted by age: " + people);
// Sort by name length using lambda expression people.sort(Comparator.comparingInt(p -> p.getName().length())); System.out.println("Sorted by name length: " + people);
// Sort by age in reverse order using default method people.sort(Comparator.comparingInt(Person::getAge).reversed()); System.out.println("Sorted by age in reverse order: " + people);
// Sort by name and then by age using default methods people.sort(Comparator.comparing(Person::getName).thenComparingInt(Person::getAge)); System.out.println("Sorted by name and then by age: " + people); } }
You can also try this code with Online Java Compiler
Sorted by name: [Person{name='Alice', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=20}, Person{name='David', age=35}]
Sorted by age: [Person{name='Charlie', age=20}, Person{name='Alice', age=25}, Person{name='Bob', age=30}, Person{name='David', age=35}]
Sorted by name length: [Person{name='Bob', age=30}, Person{name='Alice', age=25}, Person{name='David', age=35}, Person{name='Charlie', age=20}]
Sorted by age in reverse order: [Person{name='David', age=35}, Person{name='Bob', age=30}, Person{name='Alice', age=25}, Person{name='Charlie', age=20}]
Sorted by name and then by age: [Person{name='Alice', age=25}, Person{name='Bob', age=30}, Person{name='Charlie', age=20}, Person{name='David', age=35}]
In this example:
1. We define a `Person` class with `name` and `age` attributes, along with getter methods and a `toString()` method for proper representation.
2. In the `main` method, we create a list of `Person` objects.
3. We showed different ways to sort the list using Comparators: - `people.sort(Comparator.comparing(Person::getName))`: Sorts the list by name using a lambda expression that extracts the name from each Person object. - `people.sort(Comparator.comparingInt(Person::getAge))`: Sorts the list by age using a lambda expression that extracts the age from each Person object. - `people.sort(Comparator.comparingInt(p -> p.getName().length()))`: Sorts the list by the length of the name using a lambda expression. - `people.sort(Comparator.comparingInt(Person::getAge).reversed())`: Sorts the list by age in reverse order using the `reversed()` default method. - `people.sort(Comparator.comparing(Person::getName).thenComparingInt(Person::getAge))`: Sorts the list first by name and then by age using the `thenComparingInt()` default method.
4. After each sorting operation, we print the sorted list to observe the results.
Java Comparable Vs Java Comparator
Here’s a detailed difference between comparable and comparator in Java:
Parameters
Comparable
Comparator
Objective
It defines the natural order of objects within the class.
It provides custom comparison logic to sort objects.
Implementation
It requires modification of the class.
compare() method of the Comparator Interface gets overridden.
Interface
The class itself implements it.
It is implemented separately.
Method
public int compareTo(Object obj)
public int compare(Object obj1, Object obj2):
Null Values
Null values should be handled by the compareTo() method.
Null values will be handled as per the custom logic defined.
Multiple Implementation
Only one natural ordering can be defined per class.
Multiple implementations can be defined.
Frequently Asked Questions
What is Java Comparator for comparable?
Java's Comparator is used to define custom comparison logic, distinct from a class's natural ordering defined by Comparable.
What is the Comparator functional interface?
The Comparator functional interface in Java provides a way to define custom comparison logic for classes that do not implement Comparable. It allows sorting and comparison of objects based on specific criteria defined by the programmer.
Why use Comparator in Java?
The Comparable interface is used in Java to establish a natural ordering for objects of a class. This enables sorting collections of objects and provides a standard way to compare instances of the class.
Is Comparator a functional interface?
Yes, Comparator is a functional interface in Java, allowing implementation via lambda expressions.
How to compare names using Comparator in Java?
To compare names using Comparator in Java: Comparator<Person> byName = Comparator.comparing(Person::getName);
Is Comparator a functional interface?
Yes, Comparator is a functional interface because it has a single abstract method compare(T o1, T o2) used for comparison.
What is the value of Comparator in Java?
Comparator provides custom sorting logic, allowing developers to define multiple sorting strategies for objects without modifying their original class structure.
Conclusion
This article covered everything you needed to know about Java Comparator Interface in Java Collection. We covered its methods, compare and equals with examples. We also saw how to perform sorting using a comparator and the difference between comparable and comparator.