12

I was just looking at the method defined in the List interface:

Returns an array containing all of the elements in this list in the correct order; the runtime type of the returned array is that of the specified array. If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list. If the list fits in the specified array with room to spare (i.e., the array has more elements than the list), the element in the array immediately following the end of the collection is set to null. This is useful in determining the length of the list only if the caller knows that the list does not contain any null elements.

<T> T[] toArray(T[] a);

And I was just wondering why is it implemented this way, basically if you pass it an array with a length < to the list.size(), it will simply create a new one and return it. Therefore the creation of the new Array Object in the method parameter is useless.

Additionally if you pass it an array long enough using the size of the list if returns that same object with the objects - really no point in returning it since it is the same object but ok for clarity.

The problem is that I think this promotes slightly inefficient code, in my opinion toArray should simply receive the class and just return the new array with the contents.

Is there any reason why it is not coded that way?.

matt b
  • 132,562
  • 64
  • 267
  • 334
Oscar Gomez
  • 18,102
  • 12
  • 80
  • 113

5 Answers5

6
  357       public <T> T[] toArray(T[] a) {
  358           if (a.length < size)
  359               // Make a new array of a's runtime type, but my contents:
  360               return (T[]) Arrays.copyOf(elementData, size, a.getClass());
  361           System.arraycopy(elementData, 0, a, 0, size);
  362           if (a.length > size)
  363               a[size] = null;
  364           return a;
  365       }

Maybe so it has a runtime type?

From wiki:

Consequently, instantiating a Java class of a parameterized type is impossible because instantiation requires a call to a constructor, which is unavailable if the type is unknown.

dfb
  • 12,827
  • 1
  • 26
  • 52
  • 3
    Still there's no reason they don't just pass a class parameter and use that instead of a.getClass(). But then that's only inefficient if someone passes an array < size and why would one do that? – Voo Jul 01 '11 at 00:07
  • Why not pass it the class parameter and just return the array? – Oscar Gomez Jul 01 '11 at 00:48
  • 1
    @Voo actually in the List abstract class where the method is suggested, it even suggests on the comments, for you to to pass it an array with size 0, which is even more odd. – Oscar Gomez Jul 01 '11 at 00:51
5

As mentioned by others, there are a couple different reasons:

  • You need to pass in the type somehow, and passing in an array of the specified type isn't an unreasonable way to do it. Admittedly, it might be nice if there was a version that you pass in the Class of the type you want too, for speed.
  • If you want to reuse your array, you can keep passing in the same one, rather than needing to create a new one each time. This can save time and memory, and GC issues if you need to do it many, many times
RHSeeger
  • 15,196
  • 6
  • 48
  • 41
  • 1
    I guess the only benefit would be that you can reuse the array, but still I think there should be an overload with a class parameter. – Oscar Gomez Jul 01 '11 at 00:59
3

Most likely this is to allow you to reuse arrays, so you basically avoid (relatively costly) array allocation for some use cases. Another much smaller benefit is that caller can instantiate array slightly more efficiently, since toArray() must use 'java.lang.reflect.Array.newInstance' method.

StaxMan
  • 102,903
  • 28
  • 190
  • 229
2

This method is a holdover from pre-1.5 Java. Here is the link to javadoc

Back then it was the only way to convert a list to a reifiable array.

It is an obscure fact, but although you can store anything in the Object[] array, you cannot cast this array to more specific type, e.g.

Object[] generic_array = { "string" };

String[] strings_array = generic_array; // ClassCastException

Seemingly more efficient List.toArray() does just that, it creates a generic Object array.

Before Java generics, the only way to do a type-safe transfer was to have this cludge:

String[] stronglyTypedArrayFromList ( List strings )
{
    return (String[]) strings.toArray( new String[] );
    // or another variant
    // return (String[]) strings.toArray( new String[ strings.size( ) ] );
}

Thankfully generics made these kind of machinations obsolete. This method was left there to provide backward compatibility with pre 1.5 code.

Alexander Pogrebnyak
  • 42,921
  • 9
  • 97
  • 117
2

My guess is that if you already know the concrete type of T at the point you're calling toArray(T[]), it's more performant to just declare an array of whatever it is than make the List implementation call Arrays.newInstance() for you -- plus in many cases you can re-use the array.

But if it annoys you, it's easy enough to write a utility method:

public static <E> E[] ToArray(Collection<E> c, Class<E> componentType) {
    E[] array = (E[]) Array.newInstance(componentType, c.size());
    return c.toArray(array);
}

(Note that there's no way to write <E> E[] ToArray(Collection<E> c), because there's no way to create an array of E at runtime without a Class object, and no way to get a Class object for E at runtime, because the generics have been erased.)

David Moles
  • 39,436
  • 24
  • 121
  • 210