5

Why is there no toArray variant in List which only accepts a type, for example:

Foo[] array = list.toArray(Foo.class);
// or
Foo[] array = list.toArray(Foo[].class);

I have seen

// existing array
Foo[] array = list.toArray(array);
// Fake array
Foo[] array = list.toArray(new Foo[0]);

But it seems inefficient and counter-intuitive to me to create an empty array when I just want to specify the type and not create an unecessary throw-away array.

Zabuzard
  • 20,717
  • 7
  • 45
  • 67
james_t
  • 236
  • 1
  • 8
  • 1
    It's unclear what you're asking. You **can** pass an existing array to [`toArray`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/List.html#toArray(T%5B%5D)) and, if the list will fit in it, it will be used. (If not, a new one is created.) So...? – T.J. Crowder Jul 31 '19 at 17:53
  • 2
    Whether you pass in `Type[].class` or `new Type[0]`, a new object would need to be instantiated either way. The former would require reflection. – Dioxin Jul 31 '19 at 17:54
  • 1
    @VinceEmigh Since `toArray` will create a new array for you anyway if the given array is too small, it is *already* using reflection, and when you pass in `new Type[0]`, you're actually creating **2** arrays, since the zero-length array will be discarded, so passing in `Type[].class` would actually be more efficient. – Andreas Jul 31 '19 at 18:25
  • @Andreas I wasn't mentioning which was more efficient, I was just stating how either way results in a new instance, to clear any misconceptions regarding "*where perhaps a ctor will be unnecessarily used*". Yes, by passing in an array whose size is too small, `Array.newInstance` will be called (based on the last implementation I've seen of it). – Dioxin Jul 31 '19 at 18:28

1 Answers1

6

From an interface-perspective, I agree.

The method only needs the type (apart from the side-effects), so it would be appropriate to only demand the type.

The main reason is efficiency, I guess. The implementation that only takes the type is significantly slower, I did not check the implementation details though (see the benchmarks in .toArray(new MyClass[0]) or .toArray(new MyClass[myList.size()])? and the blog Arrays of Wisdom of the Ancients).

However, note that we got a new variant since Java 11 which comes closer to what you want and is also more appropriate in this situation:

toArray(Foo[]::new)

From its documentation:

Returns an array containing all of the elements in this collection, using the provided generator function to allocate the returned array.

Use toArray() to create an array whose runtime type is Object[], or use toArray(T[]) to reuse an existing array.

The default implementation calls the generator function with zero and then passes the resulting array to toArray(T[]).

The method does not need reflection since you provide the generator directly.

To summarize, nowadays you should use

  • toArray() if you want Object[] (rarely appropriate),
  • toArray(T[]) if you want to reuse an existing array (should be large enough),
  • toArray(IntFunction<T[]>) if you want type safety and a new array.
Zabuzard
  • 20,717
  • 7
  • 45
  • 67
  • 1
    *"The implementation that only takes the type is significantly slower, since it needs to use reflection to create the array dynamically"*. Which is exactly what happens when you pass in a zero-length array and `toArray` has to re-size it, and it seems a lot of people pass in zero-length arrays because they are too lazy to call the `size()` method of the collection in question. --- Still, up-vote from me. – Andreas Jul 31 '19 at 18:27
  • 2
    @Andreas Iirc someone here benchmarked it and passing in a `0` length array was significantly faster than passing in a correctly sized array. – Zabuzard Jul 31 '19 at 18:28
  • 1
    Long story short, HotSpot has an intrinsic for creating new arrays, hence the performance boost. From the linked post: [reflective intrinsics](http://hg.openjdk.java.net/jdk9/jdk9/hotspot/file/tip/src/share/vm/classfile/vmSymbols.hpp#l862) – Dioxin Jul 31 '19 at 18:40
  • @Andreas passing zero is faster than passing the actual size; if the VM can prove that allocations in the specified array will follow, it does not have to do the zeroing of the array; turns out that this _is_ faster. – Eugene Aug 01 '19 at 12:48