Comparable Vs Comparator in Java

Salitha Chathuranga
5 min readJun 13, 2022

--

Let’s sort objects as we want

Hi all, I brought you an article which is very useful while working with Java Collections framework. 😊

This is all about ordering and comparing of objects! 👊

In real day to day programming, we are dealing with classes and objects most of the times. We create so many objects and manipulate them is different manners. One of the common scenario is sorting objects. Let’s see how we can achieve that using both comparators and comparables.

As their names depicts, both are used to compare objects and order them accordingly.

Scenario: 😎

Let’s assume we need to store set of vehicles in ascending order by its manufactured year. We can consider year as a simple string here…

Our Vehicle Pojo class will be like this.

public class Vehicle {
int code;
String model;
String year;

public Vehicle(int code, String model, String year) {
this.code = code;
this.model = model;
this.year = year;
}

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public String getModel() {
return model;
}

public void setModel(String model) {
this.model = model;
}

public String getYear() {
return year;
}

public void setYear(String year) {
this.year = year;
}

@Override
public String toString() {
return "Vehicle{" +
"code=" + code +
", model='" + model + '\'' +
", year='" + year + '\'' +
'}';
}

}

Let’s dive into comparable and apply it here.

Comparable 👊

—When we use comparables, we need to perform these steps.

2️⃣ steps to follow:

  1. Implement the Pojo with Comparable interface.
  2. Override the compareTo method as we want.

Basically, we are going to modify the Vehicle class! We have to give our custom logic into compareTo method. Then when it is iterating over the list of objects, it will automatically sort the objects. It compares this(current object) and the next object. After implementing Comparator, class should be like this.

public class Vehicle implements Comparable<Vehicle>{
int code;
String model;
String year;

public Vehicle(int code, String model, String year) {
this.code = code;
this.model = model;
this.year = year;
}

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public String getModel() {
return model;
}

public void setModel(String model) {
this.model = model;
}

public String getYear() {
return year;
}

public void setYear(String year) {
this.year = year;
}

@Override
public String toString() {
return "Vehicle{" +
"code=" + code +
", model='" + model + '\'' +
", year='" + year + '\'' +
'}';
}

@Override
public int compareTo(Vehicle vehicle) {
return this.getYear().compareTo(vehicle.getYear());
}

}

See, this line simply does the job!

this.getYear().compareTo(vehicle.getYear())

Let’s setup the client code.

List<Vehicle> vehicles = Arrays.asList(
new Vehicle(567, "Civic", "2019"),
new Vehicle(234, "Corolla", "2010"),
new Vehicle(333, "Allion", "2005"),
new Vehicle(333, "Premio", "2016")
);
Collections.sort(vehicles);

See the result. They are ordered by year!

[
Vehicle{code=333, model=’Allion’, year=’2005'},
Vehicle{code=234, model=’Corolla’, year=’2010'},
Vehicle{code=333, model=’Premio’, year=’2016'},
Vehicle{code=567, model=’Civic’, year=’2019'}
]

Sort by single property — Comparables ❤️

It’s simple string comparison using String => compareTo method. If the property is an integer, we can use Integer => compareTo method OR just return the two numbers difference.

return this.getYear().compareTo(vehicle.getYear());ORreturn this.getYear() - vehicle.getYear();

— If we need need to order vehicles by their code numbers in descending order, logic will be like this…

return vehicle.getCode() - this.getCode();

NOTE: We have to swap this and vehicle to change the order from ascending to descending.

Sort by multiple properties — Comparables ❤️

If we need to sort vehicles based on 2 properties as previous???

  • Vehicles should be ordered by codes in descending order
  • If vehicle codes are equal, it should return cars ordered by year.

We can override logic as follows.

@Override
public int compareTo(Vehicle vehicle) {
if (vehicle.getCode() - this.getCode() == 0) {
return this.getYear().compareTo(vehicle.getYear());
} else {
return vehicle.getCode() - this.getCode();
}
}

After adding some objects, we SHOULD CALL sort method from collections to enable this sorting!

List<Vehicle> vehicles = Arrays.asList(
new Vehicle(567, "Civic", "2019"),
new Vehicle(234, "Corolla", "2010"),
new Vehicle(333, "Allion", "2005"),
new Vehicle(333, "Premio", "2016")
);
Collections.sort(vehicles);

Look at the result. All the cars are ordered by codes in descending order! Cars with code 333 are ordered by their year in ascending order => 2005 to 2016.

[
Vehicle{code=567, model='Civic', year='2019'},
Vehicle{code=333, model='Allion', year='2005'},
Vehicle{code=333, model='Premio', year='2016'},

Vehicle{code=234, model='Corolla', year='2010'}
]

Comparator 👊

In contrast to comparables, this is working in a separate manner.

Let’s take the same scenario: sort vehicles in ascending order by its manufactured year.

3️⃣ steps to follow:

  1. Create new class with a name like — Class+Property+Comparator.
  2. Implement the new class with Comparator interface.
  3. Override the compare method as we want.

Sort by single property — Comparators ❤️

Let’s override the method and apply the logic. Now we have to create a separate class for this. Ideally we can create that for comparing codes. I will name the class as this: VehicleCodeComparator.

public class VehicleCodeComparator implements Comparator<Vehicle> {
@Override
public int compare(Vehicle v1, Vehicle v2) {
return v1.getYear().compareTo(v2.getYear());
}
}

Since this is not in the Pojo, we cannot refer to this keyword like we did in comparables. Now it simply takes two arguments as two vehicle objects and do the comparing! In the client code, we have to do as follows.

vehicles.sort(new VehicleCodeComparator());

Client code to test:

List<Vehicle> vehicles = Arrays.asList(
new Vehicle(567, "Civic", "2019"),
new Vehicle(234, "Corolla", "2010"),
new Vehicle(333, "Allion", "2005"),
new Vehicle(333, "Premio", "2016")
);
vehicles.sort(new VehicleCodeComparator());

Same result is there — ordered by codes.. 👌

Bonus Fact!!! 💥

From Java 8, we have method references and lambda expressions. So, we can define the Comparator on the go, at runtime in the client code itself! Reason: Comparator is a Functional Interface.

No need of a separate class then.. 😮

// Lambda expressions
vehicles.sort((v1, v2) -> v1.getYear().compareTo(v2.getYear()));
// Method references
vehicles.sort(Comparator.comparing(Vehicle::getYear));

Sort by multiple properties — Comparators ❤️

If we need to sort by multiple properties as previous, how to achieve that??? We can do it at the runtime using method references..

vehicles.sort(Comparator.comparing(Vehicle::getCode).reversed()
.thenComparing(Vehicle::getYear));

See the results: 😎

[
Vehicle{code=567, model=’Civic’, year=’2019'},
Vehicle{code=333, model=’Allion’, year=’2005'},
Vehicle{code=333, model=’Premio’, year=’2016'},
Vehicle{code=234, model=’Corolla’, year=’2010'}
]

Advantages of Comparators over Comparables

  • No need to pollute the Pojo class. We can either create a new Comparator class OR implement runtime sorting.
  • We can separate out the sorting logic.
  • Follows Open/Closed principle of SOLID.

Hope you got an idea on how to use both classes. Usage is dependent on the context. Please be careful and use them accordingly!

Happy coding!!!

--

--

Salitha Chathuranga
Salitha Chathuranga

Written by Salitha Chathuranga

Associate Technical Lead at Sysco LABS | Senior Java Developer | Blogger

No responses yet