Why does Enumeration get converted to ArrayList and not List in java.utils?

JavaArraylistCollectionsReturn Type

Java Problem Overview


Is there a good reason that the Collections.list() method in the java.utils package returns an ArrayList<T> instead of List<T>?

Obviously an ArrayList is a List, but I'm under the impression that it's generally good practice to return the interface type instead of implementation type.

Java Solutions


Solution 1 - Java

Disclaimer: I am not a JDK author.

I agree that it's correct to write your own code to interfaces, but if you're going return a mutable collection to a third party, it's important to let the third party know what sort of List they're getting back.

LinkedList and ArrayList are very different, performance wise, for various operations. For example, removing the first element of an ArrayList is O(n), but removing the first element of a LinkedList is O(1).

By fully specifying the return type, the JDK authors are communicating extra information, in unambiguous code, about what sort of object they're giving back to you, so you can write your code to use this method properly. If you really need a LinkedList, you know that you have to specify one here.

Finally, the main reason to code to an interface over an implementation is if you think that the implementation will change. The JDK authors probably figure they are never going to change this method; it's never going to return a LinkedList or a Collections.UnmodifiableList. However, in most cases, you would probably still do:

List<T> list = Collections.list(enumeration);

Solution 2 - Java

When returning List, you'll be promoting program to an interface, which is a very good practice. However, this approach has its limitation. For example, you cannot use some methods that are defined for ArrayList and don't exist in the List interface - See this answer for details.

I'm quoting the API Design from the The Java™ Tutorials:

> ... It's fine to return an object of any type that implements or extends one of the collection interfaces. This can be one of the interfaces or a special-purpose type that extends or implements one of these interfaces. > > .. In one sense, return values should have the opposite behavior of input parameters: It's best to return the most specific applicable collection interface rather than the most general. For example, if you're sure that you'll always return a SortedMap, you should give the relevant method the return type of SortedMap rather than Map. SortedMap instances are more time-consuming to build than ordinary Map instances and are also more powerful. Given that your module has already invested the time to build a SortedMap, it makes good sense to give the user access to its increased power. Furthermore, the user will be able to pass the returned object to methods that demand a SortedMap, as well as those that accept any Map.

Since ArrayList is essentially an array, they are my first choice when I need to have a "collection-array". So if I want to convert enumeration to a list, my choice would be an array list.

In any other cases, it's still valid to write:

List<T> list = Collections.list(e);

Solution 3 - Java

Functions which return "exclusive ownership" of newly-created mutable objects should often be the most specific type practical; those which return immutable objects, especially if they might be shared, should often return less-specific types.

The reason for the distinction is that in the former case, an object will always be able to produce a new object of the indicated type, and since the recipient will own the object and there's no telling what actions the recipient might wish to perform, there would generally be no way for the code returning the object to know whether any alternative interface implementations could meet the recipient's needs.

In the latter case, the fact that the object is immutable means that the function may be able to identify an alternative type which can do everything a more complicated type could do given its exact content. For example, an Immutable2dMatrix interface might be implemented by an ImmutableArrayBacked2dMatrix class and an ImmutableDiagonal2dMatrix class. A function which is supposed to return a square Immutable2dMatrix could decide to return a ImmutableDiagonalMatrix instance if all the elements off the main diagonal happen to be zero, or an ImmutableArrayBackedMatrix if not. The former type would take a lot less storage space, but the recipient shouldn't care about the difference between them.

Returning Immutable2dMatrix rather than a concrete ImmutableArrayBackedMatrix allows code to choose the return type based upon what the array contains; it also means that if the code which is supposed to return the array happens to be holding a suitable implementation of Immutable2dMatrix it can simply return that, rather than having to construct a new instance. Both of these factors can be major "wins" when working with immutable objects.

When working with mutable objects, however, neither factor comes into play. The fact that a mutable array might not have any elements off the main diagonal when it's generated doesn't mean it will never have any such elements. Consequently, while an ImmutableDiagonalMatrix is effectively a subtype of an Immutable2dMatrix, a MutableDiagonalMatrix is not a subtype of a Mutable2dMatrix, since the latter could accept stores off the main diagonal while the former cannot. Further, while immutable objects often can and should be shared, mutable objects generally cannot. A function which is asked for a new mutable collection initialized with certain content will need to a create a new collection whether or not its backing store matches the type being requested.

Solution 4 - Java

There is a small overhead in calling methods var an interface rather than directly on an object.

This overhead is often no more than 1 or 2 processor instructions. The overhead of calling a method is even lower if the JIT knows that the method is final. This is not measurable for most code you and me right, but for the low level methods in java.utils may be used in some code where it is an issue.

Also as has been pointed out in other answers, the concrete type of the object that is return (even when hidden behind an interface) effects the performance of code that uses it. This change in performance can be very great, so such an extent that the calling software fails to work.

Clearly the authors of java.utils have no way to know what all the software that calls Collections.list() does with the result and no way to re-test this software if they change the implantation of Collections.list(). Therefore they are not going to change the implantation of Collections.list() to return a different type of List, even if the type system allowed it!

When writing your own software, you (hopefully) have automated test that cover all your code and a good understand of how your code interrelates include know where performance is an issue. Being able to make a change to a method, without having to change the callers is of great value while the design of the software is changing.

Therefore the two set of trade offs are very different.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTianxiang XiongView Question on Stackoverflow
Solution 1 - Javadurron597View Answer on Stackoverflow
Solution 2 - JavaMarounView Answer on Stackoverflow
Solution 3 - JavasupercatView Answer on Stackoverflow
Solution 4 - JavaIan RingroseView Answer on Stackoverflow