为什么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 最近在 Java 8没有没有比较器的简单版本,在很多情况下不需要。

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

tldr

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

这将是一个相当奇怪和不寻常的设计

集合#sort

集合,因为它可以在方法声明中指定类型边界,所以有可能强制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(空)

使用 list.sort(null) 而不是 list.sort() 显然是 设计选择 ,我认为这是一个很好的选择。如前所述,List 无法在编译时确保安全。所以唯一的选择就是为了运行时安全,这不太好。

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

您可能更愿意将其明确化,并为其提供使用自然顺序的琐碎 Comparator

list.sort(Comparator.naturalOrder());

至少 reader 会更清楚。


历史

您现在可能想知道为什么 List.sort(null) 甚至是受支持的功能以及为什么他们不要求您总是给它明确的 Comparator。我无法深入了解开发人员的想法,但我怀疑是历史原因。 JDK中有很多类似的例子,尤其是排序。

这主要是因为Java保持了向后兼容性。泛型是在 Java 5 中引入的,因此之前存在的所有与容器打交道的东西在设计时都没有考虑类型安全。

有几个高度相关的例子:

  • Arrays#sort(Object[])(因为 Java 1.2)不强制 Comparable。因此,他们不得不在 Java 5 Arrays.sort(T[], Comparator) 中添加一个额外的重载,就像 List 示例一样。
  • 方法 Collections.sort(T[], Comparator) 实际上接受 null 作为比较器。一个糟糕的设计选择,但它是在泛型还不存在的时候做出的。所以他们无法再删除此 功能
  • 有趣的是,他们决定让 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.

A 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

I don't understand the logic why list.sort() doesn't have a version with no comparator?

这个我也不太懂,估计是介绍那个方法的程序员个人喜好吧

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

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

我的简单规则是:

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

Also I am not sure in this situation it's better to call list.sort(null) to avoid an extra call, or it's still preferred to collection.sort() and avoiding the ugly null argument.

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