19

Why is a new sort method added in java.util.List in java 8 when we have a provision to sort lists using Collections.sort

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62

2 Answers2

29
  1. because it makes the API more intuitive and OO
  2. because it allows implementations of List to use a faster sorting algorithm, best suited to their internal structure. For example, ArrayList can sort its internal array without first doing a copy as the default implementation does.
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 8
    Overriding methods may not only be faster, they may enforce additional guarantees. E.g. before Java 8, applying `Collections.sort` to a `synchronizeList` or `Vector` was not safe without additional manual synchronization. Today, it delegates to `List.sort` and these collections override it with a `synchronized` method… Further, unmodifiable lists can throw an `UnsupportedOperationException` unconditionally, even if the `sort` attempt would not result in an actual change. – Holger Apr 05 '18 at 10:08
  • 1
    @Holger I knew about the second one, interesting enough I find it correct (I know I'm asking for trouble here), but my gut is saying that instead of potentially sort and check if sort has changed anything or not, simply throwing the exception is more idiomatic; unless a check against `SortedSet` would be performed for example... – Eugene Apr 05 '18 at 11:31
  • 2
    @Eugene it doesn’t make any sense to check a `List` against `SortedSet`; it’s impossible to implement both contracts correctly in one class. Further, I doubt that anyone is doing an explicit check whether sorting changed anything, that just happens implicitly, e.g. `Collections.sort(Collections.emptyList())` does not fail, likewise `Collections.emptyList().addAll(Collections.emptyList())` does nothing. Perhaps, that has been kept for compatibility. Newer constructs, e.g. `Collections.sort(List.of())` or `List.of().addAll(List.of());` do the idiomatic unconditional throwing. – Holger Apr 05 '18 at 13:01
  • 2
    @Holger Yes, the old behavior of zero elements to an empty (and unmodifiable) list is kept for compatibility. This allows latent bugs in applications, because this "works" until somebody tries to add a non-zero number of elements, at which time it blows up. The newer implementations provide a more "fail-fast" behavior. The new `sort` default method also can provide fail-fast behavior, as you observed. The old `Collections.sort` algorithm, if called on an unmodifiable list, would copy out, do the sort, and **then** blow up when copying back to the original list. – Stuart Marks Apr 05 '18 at 14:47
  • 1
    @Holger This also has a perhaps unfortunate side effect, which is that `Collections.sort` is now no longer failure idempotent. That is, if the sort fails -- for example, if there is a bug in the comparison method resulting in an exception -- sorting in-place can leave the original list corrupted. That didn't occur with the old copy-sort-copy-back technique. – Stuart Marks Apr 05 '18 at 14:50
  • @StuartMarks it did if an exception occurred in the middle of copying them back. – OrangeDog Apr 05 '18 at 14:54
  • 2
    @StuartMarks that’s a trade-off I can live with. After all, a broken comparator can corrupt the list anyway, e.g. if no exception has been thrown, which was more likely before the introduction of TimSort, so we don’t loose much compared to the old behavior (depending on how deep we dig). Being “failure idempotent” is a nice-to-have but shouldn’t sacrifice the performance of correct code in a significant amount. – Holger Apr 05 '18 at 14:54
  • 1
    @OrangeDog Sure, but an exception occurring during copy-back is almost unheard of. In most cases it doesn't even allocate memory, so even OOME is unlikely. It's still possible of course. However, exceptions thrown from the comparison method are quite likely, as you can tell from SO questions on this topic (search for "comparison method violates its general contract"). – Stuart Marks Apr 05 '18 at 15:07
  • 1
    @Holger Thanks for your thoughts on this. There is actually a bug on this, but so far we haven't felt the impetus to fix it. – Stuart Marks Apr 05 '18 at 15:10
16

JB Nizet's answer already gives you reasons why it was a good idea to add this method. The second aspect of this is:

If it is so obviously a good idea to add this method, why hasn't it been added in some earlier version?

Both the List interface and the static utility Collections were added in the same version 1.2, so it would have been possible to include it from the start.

After that opportunity had been missed, there was no way to add it any more. Adding a method to an interface was a change that would have broken backward-compatibility prior to the introduction of default-methods in Java 1.8.

Hulk
  • 6,399
  • 1
  • 30
  • 52