简体   繁体   English

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

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

I don't understand the logic why List.sort() doesn't have a version with no comparator.我不明白为什么 List.sort() 没有没有比较器的版本的逻辑。

Specially I see it's possible to call List.sort with null:特别是我看到可以用 null 调用 List.sort:
list.sort(null) , and it seems it uses the natural order for sorting. list.sort(null) ,它似乎使用自然顺序进行排序。

I noticed in my IDE Collections.sort() calls List.sort(null), so I was wondering why List.sort which seems to be more recently introduced in Java 8 doesn't have a simple version with no comparator, which in many cases is not needed.我注意到我的 IDE Collections.sort() 调用 List.sort(null),所以我想知道为什么 List.sort 似乎是最近在 ZD52387880E1EA22817A7,2D37592131 中引入的一个简单版本,其中很多没有比较器不需要案例。

Also I am not sure in this situation whether it's better to call List.sort(null) to avoid an extra call, or if it's still preferred to call Collections.sort() and avoid the ugly null argument.另外我不确定在这种情况下是否最好调用 List.sort(null) 以避免额外的调用,或者是否仍然更喜欢调用 Collections.sort() 并避免丑陋的 null 参数。

tldr tldr

It is a design decision, mixed with some historical reasons.这是一个设计决定,混合了一些历史原因。 It would be possible to add a list.sort() but it could not ensure safety at compile-time, only runtime.可以添加list.sort()但它不能确保编译时的安全,只有运行时。 Which would be a rather weird and unusual design for the JDK.对于 JDK 来说,这将是一个相当奇怪和不寻常的设计。


Collections#sort集合#sort

Collections, because it can specify type bounds in the method declaration, has the possibility to force Comparable elements: Collections,因为它可以在方法声明中指定类型边界,所以有可能强制Comparable元素:

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

So the method can ensure that the collection contains Comparable elements.因此该方法可以确保集合包含Comparable元素。 So a Comparator is not needed.所以不需要Comparator器。


List#sort列表#排序

List can not do that. List无法做到这一点。 Its generic type must allow everything, You have to be able to use a List<Dog> although those are maybe not Comparable .它的泛型类型必须允许一切,您必须能够使用List<Dog>尽管那些可能不是Comparable

So if there would be a list.sort() , then this method would need to figure out whether the lists generic type T is Comparable or not.因此,如果存在list.sort() ,那么此方法将需要确定列表泛型类型T是否为Comparable But that information is only present on runtime, we want it at compile-time for a nice design though.但是这些信息只存在于运行时,我们希望它在编译时得到一个好的设计。

Because this would lead to a bad design, list.sort() does not exist.因为这会导致糟糕的设计, list.sort()不存在。 Hence the extra method forcing a Comparator which can ensure safety at compile-time..因此,强制使用Comparator的额外方法可以确保编译时的安全性。


list.sort(null) list.sort(null)

Having list.sort(null) instead of list.sort() is clearly a design choice , a good one in my opinion. list.sort(null)而不是list.sort()显然是一种设计选择,在我看来是一个很好的选择。 As said before, it is not possible for List to ensure safety at compile-time.如前所述, List无法在编译时确保安全。 So the only choice is to go for runtime safety, which is not as good.因此,为了运行时安全性,唯一的选择是 go,这不是那么好。

Having a list.sort() that only sometimes works and throws an exception otherwise would be a weird design choice.有一个list.sort()只在某些时候有效并抛出异常,否则将是一个奇怪的设计选择。 A call like list.sort(null) however is much clearer to any user.然而,像list.sort(null)这样的调用对任何用户来说都更加清晰。 Especially, it is clearer that this will be covered by runtime decisions, not by compile-time checks.特别是,更清楚的是,这将被运行时决策覆盖,而不是编译时检查。

You might want to prefer to make it explicit and give it the trivial Comparator that uses the natural ordering:您可能希望使其显式化并为其提供使用自然排序的普通Comparator

list.sort(Comparator.naturalOrder());

That would at least be even clearer to a reader.这至少对读者来说会更清楚。


History历史

You might wonder now why List.sort(null) even is a supported feature and why they do not require you to give it an explicit Comparator always.您现在可能想知道为什么List.sort(null)甚至是受支持的功能,以及为什么它们不要求您始终为它提供显式Comparator I can not look into the developers mind but I suspect historical reasons.我无法调查开发人员的想法,但我怀疑是历史原因。 There are quite a few similar examples in the JDK, especially with sorting. JDK 中有很多类似的示例,尤其是排序方面。

This is mainly because Java maintains backwards compatibility.这主要是因为 Java 保持了向后兼容性。 Generics were introduced with Java 5, so everything that existed before which was dealing with containers was not designed with type safety in mind. Generics 是在 Java 5 中引入的,因此之前存在的处理容器的所有内容在设计时都没有考虑到类型安全。

There are a couple of highly related examples:有几个高度相关的例子:

  • Arrays#sort(Object[]) (since Java 1.2) does not force Comparable . Arrays#sort(Object[]) (自 Java 1.2 起)不强制Comparable Therefore, they had to add an extra overload in Java 5 Arrays.sort(T[], Comparator) , exactly like the List example.因此,他们必须在 Java 5 Arrays.sort(T[], Comparator)中添加额外的重载,就像List示例一样。
  • The method Collections.sort(T[], Comparator) actually accepts null for the comparator.方法Collections.sort(T[], Comparator)实际上接受null作为比较器。 A bad design choice, but it was made back then when generics did not exist yet.一个糟糕的设计选择,但它是在 generics 还不存在时做出的。 So they could not remove this feature anymore.所以他们不能再删除这个功能了。
  • Interestingly they made the decision to let List.sort(Comparator) , a new method, also support null .有趣的是,他们决定让新方法List.sort(Comparator)也支持null However, in Stream.sort(Comparator) they did not.然而,在Stream.sort(Comparator)他们没有。 This is a weird inconsistency in the JDK.这是 JDK 中一个奇怪的不一致。

When you provide no comparator, the natural ordering is used, which is only applicable for types which implement Comparable .当您不提供比较器时,将使用natural排序,这仅适用于实现Comparable的类型。

If you look at the method definition for Collections.sort() :如果您查看Collections.sort()的方法定义:

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

The element type is scoped such that it must implement Comparable .元素类型的范围是这样的,它必须实现Comparable

A List does not have that constraint, as there are plenty of cases where you want a list to hold things without a natural order. List没有这种约束,因为在很多情况下,您希望列表保存没有自然顺序的事物。 As such, you can't call sort() , because the List can't guarantee that it's holding elements with a natural ordering.因此,您不能调用sort() ,因为List不能保证它包含具有自然顺序的元素。

You can test this:您可以对此进行测试:

The following is a compile error:以下是编译错误:

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

    Collections.sort(objs);

because Object does not implement Comparable<Object> , there is no sensible way to sort them unless you provide a comparator.因为Object没有实现Comparable<Object> ,所以除非您提供比较器,否则没有明智的方法对它们进行排序。

If I bypass the Collections class and just do this:如果我绕过Collections class 并这样做:

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

    objs.sort(null);

I get an exception, that explains the same:我得到一个例外,解释相同:

    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?我不明白为什么 list.sort() 没有没有比较器的版本的逻辑?

That's what I also don't understand, but I guess it is just a personal preference of the programmer who introduced that method.这也是我不明白的地方,但我想这只是介绍该方法的程序员的个人喜好。

Since Java 1.8 supports default methods in interfaces it would have been easy to add a overloaded version without a parameter List.sort() to make clear that the parameter is optional.由于 Java 1.8 支持接口中的默认方法,因此很容易添加不带参数List.sort()的重载版本以明确该参数是可选的。 This method could simply delegate to the sort(Comparator) method.该方法可以简单地委托给sort(Comparator)方法。

I usually introduce overloaded methods to make clear which parameters are optional and which are not.我通常会引入重载方法来明确哪些参数是可选的,哪些不是。

My simple rule is:我的简单规则是:

  • A method specifies a parameter, because it needs it.方法指定一个参数,因为它需要它。 Thus it is mandatory by default.因此,默认情况下它是强制性的。
  • Provide a overloaded method without that parameter if it is optional.如果它是可选的,则提供不带该参数的重载方法。

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

I wouldn't worry about that, because if that one extra call is your problem than you are really in trouble.我不会担心这一点,因为如果那一个额外的电话是您的问题,那么您就真的有麻烦了。 So choose a version that you think is more understandable.所以选择一个你认为更容易理解的版本。 It is also possible that the JIT-compiler removes the call by inlining. JIT 编译器也有可能通过内联删除调用。

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

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