Reputation: 173
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?
Upvotes: 1
Views: 684
Reputation: 29814
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 implement Comparable
, etc...
inheritance will work as expected and be more readable: if you define a Dog
that extends Animal
, you can implement comparison for Dog
using the super implementation (i.e. a Dog is compared like any other Animal
) or overriding the implementation to implement a behavior specific to Dog
. The user of your Dog
class simply calls instance.compareTo(...)
without having to worry about what final static comparator she/he should call
users of your Animal
API know they have to implement Comparable
when adding their own animal to the inheritance tree
Upvotes: 0
Reputation: 28978
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
compareTo
method 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).
Upvotes: 4