Sunday, 20 September 2009

Item 25: Prefer lists to arrays

The main point put forward in this item is that one should prefer Lists to arrays in certain situations. Specifically, it highlights some of the problems one can see when mixing use of Lists and Java arrays, and suggests that using Lists exclusively is the best way to avoid these problems. So, this item isn’t about other aspects of arrays vs. Lists such as being of fixed length vs. coping with addition etc., as one might have guessed from the headline.

The author points out a couple of major differences between arrays and generic Lists (List<T>). First of all, arrays are covariant, whereas Lists are not. So, String[] is a subtype of Object[], whereas List<String> is not a subtype of List<Object>. The latter aspect of generic types may seem counter-intuitive at first, but the author explains that this can be seen as a benefit: it is safer as it avoids cases where objects of the wrong type can be inserted into the generic collection. (Templated types in C++ are also not covariant)

The other major difference is that arrays are reified, whereas generic collections are not. This means that arrays have information at runtime about the type of their contained items whereas generic collections don’t. Another term for describing this aspect of generic types is "type erasure", as mentioned elsewhere on this list. I guess it’s less obvious if the type erasure of generic collections is an advantage, I’m sure many would say it’s definitely not. But the reason things were implemented in this way is to allow generic collections in generics-aware Java code to interoperate with older code. Personally, I think that was a sensible choice; while limiting some aspects of generics, it provided an easy transition path and allows mixing older and newer code. A clean break upgrade sounds like it would have been very painful and would presumably have left a lot of organisations stuck in the pre-Java 1.5 world.

These differences between arrays and generic collections (Lists discussed in particular) lead to some problems when mixing use of the two. For example, it’s not possible to create typed arrays from generic types in a statement like this inside a generic method or class:

List<E> list = ...
E[] snapshot = list.toArray();

You can make this compile using casts but these are not safe and will produce warnings:

E[] snapshot = (E[])list.toArray();

The reason it’s not safe is that at runtime, there is no type information about the list, hence the created array is basically an Object[].

The author suggests avoiding mixing arrays and Lists in this way by only using Lists. Which makes sense in some cases, but I’m sure there are cases where the advantages of arrays (speed?) mean that they will be preferable, or there’s legacy code or other code outside a developer’s control that uses arrays. In which case, one would have to deal with the resulting complications.

Jan Stette

No comments: