我什么时候应该使用 Comparator 和 Comparable?

When should I use Comparator vs Comparable?

我有一个需要以某种方式排序的 POJO 列表。我在 POJO class 中定义了一个 Comprator 并用它来对列表进行排序。

下面的方式correct/best是实践吗?有更好的方法吗?

    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;
    }
}

顺便说一句,输出是正确的。

编辑 添加更 classic 的方式:

    @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);
        }
    }

您认为哪一个是更好的解决方案?

ComparatorComparable 的用例之间有很大区别。

实现 Comparable 接口适用于在您的域模型中具有 自然顺序 的对象。我不确定动物是否具有 自然顺序 ,但如果从您的应用程序如何为动物建模的角度来看是这样,那很好 - 这就是要走的路。否则,您的 class 不应实施 Comparable.

这不是什么opinion-based,文档 明确定义了这些接口的使用时间。

Comparable:

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.

Comparator:

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.

另一个明显的区别是,您可以根据需要定义任意多种比较器。当没有一种特定的方法来比较和排序对象时,这很方便。而且他们的名字肯定比comparator.

更有意义

就我个人而言,我认为将几个 比较器 定义为 public static final 字段并没有什么大的危害,如您的示例所示。如果你有一个单独的 class 来管理这种类型的实例 - 将 比较器 提取到那个 class 中,否则如果这些对象无处不在并在许多地方使用您可以将它们留在 POJO 中(基于意见的部分)。

这不是基于意见的:TL;DR 实现 Comparable

  • 语义上,这就是接口的设计目的:它们表达了对象强制执行的契约,对象的行为:如果对象是可序列化的,那么它们应该实现 Serializable ,如果它们具有可比性,那么它们应该实现 Comparable,等等...

  • 继承将按预期工作并且更具可读性:如果定义扩展 AnimalDog,则可以使用 super 实现 Dog 的比较实现(即 Dog 与任何其他 Animal 进行比较)或覆盖实现以实现特定于 Dog 的行为。 Dog class 的用户只需调用 instance.compareTo(...) 而不必担心最终静态比较器 she/he 应该调用什么

  • 您的 Animal API 的用户知道他们在将自己的动物添加到继承树时必须实施 Comparable