Wednesday, October 12, 2005

Arrays and Collections

Keith Devens makes a good point of recommending Arrays.asList(...) over hand-crafted lists. Thus he suggests that in place of:

List<Foo> foolist = new ArrayList<Foo>();
foolist.add(foo);
return foolist;

You instead use:

return Arrays.asList(foo);

This has much to recommend it, especially conciseness and clarity. However, as the hawker on late night TV says, But there's more!

In addition to the useful Arrays class of static helper methods, there is Collections which has more useful static helper methods. So the example could have also been written as:

return Collections.singletonList(foo);

What's the difference? This is an interesting question. There is no way to select between Arrays and Collections without further information.

If the requirement were to return an unmodifiable view, then the easy route is:

return Collections.singletonList(foo);

Which is an even more clear way to express the intention of the code than Arrays.toList—the singleton pattern is one of the first grokked by any programmer. What if the code fragment were part of this method?

/**
 * Returns a modifiable list of just the topmost foo.
 *
 * @return Modifiable list containing just the topmost foo
 */
public List<Foo> copyTopFooAsList() {
    return ???;
}

Now it is clear from the javadocs for Arrays.asList that this is not quite what we want:

Returns a fixed-size list backed by the specified array. (Changes to the returned list "write through" to the array.) This method acts as bridge between array-based and collection-based APIs, in combination with Collection.toArray.

And clearly Collections.singletonList(T) is not right either—when is a proper singleton modifiable? And Collections.toArray is clearly useful, but not what we are looking for (it returns an array, not a list).

After some thought, the solution is not hard to find: look at the constructors for ArrayList, and at ArrayList(Collection) in particular. Although the javadocs do not explicitly say copies from the input, they do hint at that (and a quick peek at the source code confirms it is so):

Constructs a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.

So the solution becomes:

return new ArrayList(Collections.singleton(foo));

A handy one-liner to keep in the back pocket for future use.

2 comments:

MÃ¥rten Gustafson said...

Personally I'd just return:
Collections.unmodifiableList(foo);

But I assume that the point of your post was to return a, modifiable, copy.


Great blog btw, keep it up :)

Brian Oxley said...

Yah, the javadocs for the sample method do require modifiability.

Good practice usually disallows such returns, however, so the example is a little far-fetched.