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

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

Obviously, ArrayList is a List , but I get the impression that it is usually recommended to return an interface type instead of an implementation type.

+74
java collections arraylist return-type
Sep 02 '15 at 15:05
source share
4 answers

Disclaimer: I am not the author of JDK.

I agree that it is correct to write your own code for interfaces, but if you return the collection to a mutable third party, it is important to tell the third party what type of List they are returned.

LinkedList and ArrayList very different in terms of performance for different operations. For example, deleting the first element of an ArrayList is O(n) , but deleting the first element of a LinkedList is O(1) .

Having fully specified the return type, the JDK authors transmit additional information , in a unique code, about which object they return to you, so you can write your code to use this method correctly. If you really need a LinkedList , you know that you have to specify it here.

Finally, the main reason for the code for the interface over the implementation is if you think that the implementation will change. JDK authors probably believe that they will never modify this method; it will never return a LinkedList or Collections.UnmodifiableList . However, in most cases, you are likely to still do:

 List<T> list = Collections.list(enumeration); 
+54
Sep 02 '15 at 15:24
source share

When you return List you will push the program to the interface, which is very good practice. However, this approach has its limitations. For example, you cannot use some methods that are defined for ArrayList and do not exist in the List interface. See this answer for more details.

I quote from Design API from Java ™ Tutorials:

... It's great to return an object of any type that implements or extends one of the collection interfaces. It can be one of the interfaces or a special type that extends or implements one of these interfaces.

.. In a sense, the return values ​​should have the opposite behavior of the input parameters: it is best to return the most specific applicable collection interface, rather than the most general. For example, if you are sure that you will always return SortedMap , you must provide the corresponding method with the return type SortedMap , not Map . SortedMap instances take longer than regular Map instances, as well as more powerful ones. Considering that your module has already invested time in creating SortedMap , it makes sense to give the user access to his increased power. In addition, the user will be able to pass the returned object to methods that require a SortedMap , as well as those that accept any Map .

Since an ArrayList is essentially an array, this is my first choice when I need to have an array-set. Therefore, if I want to convert an enumeration to a list, my choice is a list of arrays.

In any other cases, it is still valid for writing:

 List<T> list = Collections.list(e); 
+31
02 Sep '15 at 15:24
source share

Functions that return the "exclusive ownership" of newly created mutable objects often need to be the most specific type of practical; those that return immutable objects, especially if they can be split, should often return less specific types.

The reason for the difference is that in the first case, the object will always be able to create a new object of the specified type, and since the recipient will own the object and it will not say what actions the recipient may wish to perform, there will usually be no way for the code returning the object to know whether any alternative implementations of the interface can satisfy the needs of the recipient.

In the latter case, the fact that the object is immutable means that the function can identify an alternative type that can do anything that a more complex type can do, given its exact contents. For example, the Immutable2dMatrix interface can be implemented by the ImmutableArrayBacked2dMatrix and ImmutableDiagonal2dMatrix . A function that should return an Immutable2dMatrix square may decide to return an ImmutableDiagonalMatrix instance if all elements with the main diagonal are zero or ImmutableArrayBackedMatrix if not. The first type will take up much less storage space, but the recipient should not care about the difference between the two.

Returning Immutable2dMatrix rather than a specific ImmutableArrayBackedMatrix allows the code to select a return type based on what the array contains; it also means that if the code that should return the array has a suitable Immutable2dMatrix implementation, it can simply return it, instead of creating a new instance. Both of these factors can be the main “victories” when working with immutable objects.

However, when working with mutable objects, no one plays a role. The fact that the variable array may not have any elements from the main diagonal when creating it does not mean that it will never have such elements. Therefore, although a ImmutableDiagonalMatrix is actually a subtype of a Immutable2dMatrix , a MutableDiagonalMatrix not a subtype of a Mutable2dMatrix , since the latter can accept stores from the main diagonal, while the former cannot. In addition, although immutable objects can often and should be shared, mutable objects cannot at all. The function requested for a new mutable collection initialized with specific content must create a new collection, regardless of whether its support store matches the requested type.

+6
Sep 02 '15 at 18:48
source share

At the top is a little overhead when calling the var methods of the interface, not directly to the object.

These overheads often amount to no more than 1 or 2 processor instructions. The overhead of invoking a method is even lower if the JIT knows that the method is final. This is not measurable for most of the codes that you and I are correct, but for low-level methods in java.utils can be used in some code where this is a problem.

Also, as pointed out in other answers, the specific type of the returned object (even if it is hidden behind the interface) affects the performance of the code that uses it. This performance change can be very large, so the degree to which the calling software does not work.

It’s clear that the authors of java.utils don’t know what the software that calls Collections.list () does with the result and cannot re-test this software if they change the implantation of Collections.list (), so they are not going to change the implantation Collections.list () to return another type of List, even if the type system allowed this!

When writing your own software, you (I hope) have automatic testing that covers all your code and understands how your codes are interconnected, know where the performance problem is. The ability to change the method, without the need to change callers, is of great importance when changing the design of the software.

Therefore, the two sets of compromises are very different.

+1
Sep 03 '15 at 8:29
source share



All Articles