In the class, you just finished learning about comparing Java Objects and sorting them using Merge Sort. But is it all to explain you what exactly Comparator and Comparable is? No, it isn’t. So let’s get to understand what this entire shit is all about.
Setting up the Stage
A deeper understanding of object comparison and sorting in Java is required beyond merely applying Merge Sort and basic sorting techniques. The concepts of Comparable
and Comparator
will now be thoroughly explored.
A class named Car
is given, containing specific fields and methods. The object is mapped to a custom class in Java, which includes the following attributes:
Field Name Data Type Description
- modelName — String — Name of the Model of Car
- price — int — Price of the Car
- colors — Enum — Colors Enumeration of colors of the Car
The fields are implemented as follows:
public class Car {
private String modelName;
private int price;
private Colors colorsEnum;
// Getters and Setters for the same.
}
The enumeration for the Colors
type must also be defined:
public enum Colors {
RED(4), GREEN(2), BLUE(3), TEAL(5), WHITE(1), BLACK(0);
private int preferenceNumber;
Colors(int preferenceNumber) {
this.preferenceNumber = preferenceNumber;
}
public int getPreferenceNumber() {
return preferenceNumber;
}
}
A set of predefined colors has been included in the enumeration, with each assigned a preference number that signifies its popularity among car owners.
Problem Statement
Given the Car
class and its attributes, the following sorting operations must be performed:
- Sorting the list of
Car
objects by price in ascending order. - Sorting the list of
Car
objects by color preference in ascending order. - Sorting the list of
Car
objects by the length of the color name.
To accomplish these tasks, Comparable
and Comparator
will be applied, and their use cases will be analyzed.
The Comparable
Interface
The Comparable
interface is designed to impose a total ordering on the objects of a class. This ordering is referred to as the class’s natural ordering. The interface provides a compareTo
method, which defines the comparison logic.
Lists and arrays of objects implementing this interface can be sorted automatically using Collections.sort()
and Arrays.sort()
. By implementing Comparable
, a class is able to compare its instances to other objects of the same type. The compareTo()
method determines the sorting order based on its return value:
- A negative value indicates that the current object is less than the specified object.
- A zero value signifies equality between the two objects.
- A positive value indicates that the current object is greater than the specified object.
Since the sorting strategy is embedded within the class itself, Comparable
is preferred when a single natural ordering is required. Integer-based comparisons (such as sorting by price) are well-suited for this interface.
Implementing Comparable
for Price Sorting
public class Car implements Comparable<Car> {
private String modelName;
private int price;
private Colors colorsEnum;
@Override
public int compareTo(Car other) {
return Integer.compare(this.price, other.price);
}
}
With this implementation, any list of Car
objects will be sorted in ascending order of price when Collections.sort()
is applied.
The Comparator
Interface
The Comparator
interface provides an alternative mechanism for defining sorting logic. It enables the imposition of total ordering on collections of objects, without modifying the class itself. This is particularly beneficial when multiple sorting strategies are required or when sorting behavior must be applied to third-party classes.
Sorting operations, such as filtering based on user preferences on an e-commerce website, are efficiently handled using Comparator
. Unlike Comparable
, it allows external customization of sorting logic. The compare()
method is utilized, which behaves similarly to compareTo()
but accepts two objects as parameters.
Implementing Comparator
for Sorting by Color Preference
import java.util.Comparator;
class ColorPreferenceComparator implements Comparator<Car> {
@Override
public int compare(Car c1, Car c2) {
return Integer.compare(c1.colorsEnum.getPreferenceNumber(), c2.colorsEnum.getPreferenceNumber());
}
}
With this comparator, sorting by color preference in ascending order is achieved without modifying the Car
class.
Implementing Comparator
for Sorting by Length of Color Name
class ColorNameLengthComparator implements Comparator<Car> {
@Override
public int compare(Car c1, Car c2) {
return Integer.compare(c1.colorsEnum.name().length(), c2.colorsEnum.name().length());
}
}
This comparator enables sorting based on the length of the color name.
Applying Sorting Operations
Once the Comparator
implementations have been defined, sorting can be performed dynamically:
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Car> cars = Arrays.asList(
new Car("Tesla", 50000, Colors.RED),
new Car("BMW", 45000, Colors.BLACK),
new Car("Audi", 48000, Colors.WHITE)
);
// Sorting by price using Comparable
Collections.sort(cars);
// Sorting by color preference using Comparator
Collections.sort(cars, new ColorPreferenceComparator());
// Sorting by length of color name
Collections.sort(cars, new ColorNameLengthComparator());
}
}
Summary of Comparable
vs Comparator
Conclusion
When a natural ordering is required and sorting remains fixed across multiple use cases, Comparable
should be preferred. If multiple sorting strategies are necessary or external sorting logic is desired, Comparator
provides a more flexible approach. By understanding the appropriate scenarios for each, sorting can be optimized for efficiency and clarity.
Thus, Comparable
and Comparator
are not merely theoretical concepts but essential tools for implementing structured and well-organized sorting strategies in Java. Sorting operations, when designed correctly, can significantly enhance the functionality and maintainability of Java applications.