'When should I use Comparator vs Comparable?
I have a list of POJOs I need to sort somehow. I define a Comprator inside the POJO class and use it to sort the list.
Is the following way correct/best practice? Is there a better way to do it?
public class CompratorTest {
public static void main(String[] args) {
List<Person> people = List.of(
new Person("zoe", "saturday", 40),
new Person("luca", "red", 15),
new Person("boris", "vin", 54),
new Person("boris", "apple", 33),
new Person("boris", "apple", 70)
);
List<Person> sortedPeople =
people.stream()
.sorted((person, other) -> Person.COMPARATOR.compare(person, other))
.collect(Collectors.toList());
sortedPeople.forEach(System.out::println);
}
@Data
@AllArgsConstructor
static
class Person {
final static Comparator<Person> COMPARATOR =
Comparator.comparing((Person person) -> person.getName())
.thenComparing(person -> person.getSurname())
.thenComparing(person -> person.getAge());
String name;
String surname;
int age;
}
}
Output is correct, by the way.
EDIT Adding a more classic way:
@Data
@AllArgsConstructor
static class Animal implements Comparable<Animal> {
String name;
String race;
@Override
public int compareTo(Animal other) {
if (this.name.equals(other.name)) {
return String.CASE_INSENSITIVE_ORDER.compare(this.race, other.race);
}
return String.CASE_INSENSITIVE_ORDER.compare(this.name, other.name);
}
}
Which one do you think is a better solution?
Solution 1:[1]
There's a substantial distinction between the use cases for Comparator and Comparable.
Implementing the Comparable interface is suitable for objects that have a natural order in your domain model. I'm not sure whether animals have a natural order, but if it is the case from the perspective of how your application model the animals, that's fine - that's the way to go. Otherwise, your class should not implement Comparable.
It's not something opinion-based, documentation clearly defines when these interfaces are intended to be used.
This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's
compareTomethod is referred to as its natural comparison method.
Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for collections of objects that don't have a natural ordering.
Another obvious distinction, that you can define as many flavors of comparators as you need. Which is handy when there's no one specific way to compare and sort the objects. And they must have more meaningful names than comparator.
Personally, I don't see a huge harm in defining a couple of comparators as public static final fields, as in your example. If you have a single class that manages the instances of this type - extract the comparators into that class, otherwise if these objects are ubiquitous and used in many places you can leave them right inside the POJO (that an opinion based part).
Solution 2:[2]
This is not opinion based: TL;DR implement Comparable:
semantically, this is what Interfaces are designed for: they express a contract enforced by an object, a behavior of the object: if the objects are serializable, then they should implement
Serializable, if they are comparable, then they should implementComparable, etc...inheritance will work as expected and be more readable: if you define a
Dogthat extendsAnimal, you can implement comparison forDogusing the super implementation (i.e. a Dog is compared like any otherAnimal) or overriding the implementation to implement a behavior specific toDog. The user of yourDogclass simply callsinstance.compareTo(...)without having to worry about what final static comparator she/he should callusers of your
AnimalAPI know they have to implementComparablewhen adding their own animal to the inheritance tree
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
