繁体   English   中英

为什么 Collections.sort 可以不带比较器,但 List.sort 必须带比较器?

[英]Why can Collections.sort take no comparator but List.sort must take a comparator?

我不明白为什么 List.sort() 没有没有比较器的版本的逻辑。

特别是我看到可以用 null 调用 List.sort:
list.sort(null) ,它似乎使用自然顺序进行排序。

我注意到我的 IDE Collections.sort() 调用 List.sort(null),所以我想知道为什么 List.sort 似乎是最近在 ZD52387880E1EA22817A7,2D37592131 中引入的一个简单版本,其中很多没有比较器不需要案例。

另外我不确定在这种情况下是否最好调用 List.sort(null) 以避免额外的调用,或者是否仍然更喜欢调用 Collections.sort() 并避免丑陋的 null 参数。

tldr

这是一个设计决定,混合了一些历史原因。 可以添加list.sort()但它不能确保编译时的安全,只有运行时。 对于 JDK 来说,这将是一个相当奇怪和不寻常的设计。


集合#sort

Collections,因为它可以在方法声明中指定类型边界,所以有可能强制Comparable元素:

public static <T extends Comparable<? super T>> void sort(List<T> list)

因此该方法可以确保集合包含Comparable元素。 所以不需要Comparator器。


列表#排序

List无法做到这一点。 它的泛型类型必须允许一切,您必须能够使用List<Dog>尽管那些可能不是Comparable

因此,如果存在list.sort() ,那么此方法将需要确定列表泛型类型T是否为Comparable 但是这些信息只存在于运行时,我们希望它在编译时得到一个好的设计。

因为这会导致糟糕的设计, list.sort()不存在。 因此,强制使用Comparator的额外方法可以确保编译时的安全性。


list.sort(null)

list.sort(null)而不是list.sort()显然是一种设计选择,在我看来是一个很好的选择。 如前所述, List无法在编译时确保安全。 因此,为了运行时安全性,唯一的选择是 go,这不是那么好。

有一个list.sort()只在某些时候有效并抛出异常,否则将是一个奇怪的设计选择。 然而,像list.sort(null)这样的调用对任何用户来说都更加清晰。 特别是,更清楚的是,这将被运行时决策覆盖,而不是编译时检查。

您可能希望使其显式化并为其提供使用自然排序的普通Comparator

list.sort(Comparator.naturalOrder());

这至少对读者来说会更清楚。


历史

您现在可能想知道为什么List.sort(null)甚至是受支持的功能,以及为什么它们不要求您始终为它提供显式Comparator 我无法调查开发人员的想法,但我怀疑是历史原因。 JDK 中有很多类似的示例,尤其是排序方面。

这主要是因为 Java 保持了向后兼容性。 Generics 是在 Java 5 中引入的,因此之前存在的处理容器的所有内容在设计时都没有考虑到类型安全。

有几个高度相关的例子:

  • Arrays#sort(Object[]) (自 Java 1.2 起)不强制Comparable 因此,他们必须在 Java 5 Arrays.sort(T[], Comparator)中添加额外的重载,就像List示例一样。
  • 方法Collections.sort(T[], Comparator)实际上接受null作为比较器。 一个糟糕的设计选择,但它是在 generics 还不存在时做出的。 所以他们不能再删除这个功能了。
  • 有趣的是,他们决定让新方法List.sort(Comparator)也支持null 然而,在Stream.sort(Comparator)他们没有。 这是 JDK 中一个奇怪的不一致。

当您不提供比较器时,将使用natural排序,这仅适用于实现Comparable的类型。

如果您查看Collections.sort()的方法定义:

    public static <T extends Comparable<? super T>> void sort(List<T> list)

元素类型的范围是这样的,它必须实现Comparable

List没有这种约束,因为在很多情况下,您希望列表保存没有自然顺序的事物。 因此,您不能调用sort() ,因为List不能保证它包含具有自然顺序的元素。

您可以对此进行测试:

以下是编译错误:

    List<Object> objs = new ArrayList<>();
    objs.add(new Object());
    objs.add(new Object());

    Collections.sort(objs);

因为Object没有实现Comparable<Object> ,所以除非您提供比较器,否则没有明智的方法对它们进行排序。

如果我绕过Collections class 并这样做:

    List<Object> objs = new ArrayList<>();
    objs.add(new Object());
    objs.add(new Object());

    objs.sort(null);

我得到一个例外,解释相同:

    java.lang.ClassCastException: class java.lang.Object cannot be cast to class java.lang.Comparable

我不明白为什么 list.sort() 没有没有比较器的版本的逻辑?

这也是我不明白的地方,但我想这只是介绍该方法的程序员的个人喜好。

由于 Java 1.8 支持接口中的默认方法,因此很容易添加不带参数List.sort()的重载版本以明确该参数是可选的。 该方法可以简单地委托给sort(Comparator)方法。

我通常会引入重载方法来明确哪些参数是可选的,哪些不是。

我的简单规则是:

  • 方法指定一个参数,因为它需要它。 因此,默认情况下它是强制性的。
  • 如果它是可选的,则提供不带该参数的重载方法。

另外我不确定在这种情况下最好调用 list.sort(null) 以避免额外的调用,或者仍然首选 collection.sort() 并避免丑陋的 null 参数。

我不会担心这一点,因为如果那一个额外的电话是您的问题,那么您就真的有麻烦了。 所以选择一个你认为更容易理解的版本。 JIT 编译器也有可能通过内联删除调用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM