简体   繁体   中英

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.

Specially I see it's possible to call List.sort with null:
list.sort(null) , and it seems it uses the natural order for sorting.

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.

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.

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. Which would be a rather weird and unusual design for the JDK.


Collections#sort

Collections, because it can specify type bounds in the method declaration, has the possibility to force Comparable elements:

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

So the method can ensure that the collection contains Comparable elements. So a Comparator is not needed.


List#sort

List can not do that. Its generic type must allow everything, You have to be able to use a List<Dog> although those are maybe not 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. 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. Hence the extra method forcing a Comparator which can ensure safety at compile-time..


list.sort(null)

Having list.sort(null) instead of list.sort() is clearly a design choice , a good one in my opinion. As said before, it is not possible for List to ensure safety at compile-time. So the only choice is to go for runtime safety, which is not as good.

Having a list.sort() that only sometimes works and throws an exception otherwise would be a weird design choice. A call like list.sort(null) however is much clearer to any user. 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:

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. 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.

This is mainly because Java maintains backwards compatibility. Generics were introduced with Java 5, so everything that existed before which was dealing with containers was not designed with type safety in mind.

There are a couple of highly related examples:

  • Arrays#sort(Object[]) (since Java 1.2) does not force Comparable . Therefore, they had to add an extra overload in Java 5 Arrays.sort(T[], Comparator) , exactly like the List example.
  • The method Collections.sort(T[], Comparator) actually accepts null for the comparator. A bad design choice, but it was made back then when generics did not exist yet. So they could not remove this feature anymore.
  • Interestingly they made the decision to let List.sort(Comparator) , a new method, also support null . However, in Stream.sort(Comparator) they did not. This is a weird inconsistency in the JDK.

When you provide no comparator, the natural ordering is used, which is only applicable for types which implement Comparable .

If you look at the method definition for 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 .

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. As such, you can't call sort() , because the List can't guarantee that it's holding elements with a natural ordering.

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.

If I bypass the Collections class and just do this:

    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?

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. This method could simply delegate to the sort(Comparator) method.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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