List.toArray
, with no arguments, returns a Object[]
, not a T[]
.
Object[] toArray()
That's because, in Java, writing new T[]
(where T
is a generic type) is not allowed, since it would require runtime access to the type T
and we can't have that due to type erasure.
So they provide an overload to get around that. The overload that you're using looks like this.
<T> T[] toArray(T[] a)
You have to allocate the array and you have to make the new
call, since you know what type of array you want. If you give Java an array of the appropriate type, it can use that array. If there's not enough elements in that array, it can use reflection to generate a bigger one of the same type.
So all you're doing is telling Java, at runtime, that the type of array you want is an array containing int[]
(or a int[][]
).
Now, as for why we make an array of zero elements, that's a bit more complicated (and I don't pretend to fully understand the optimizations in play either), but it's explained in this blog post. There would certainly be nothing wrong with giving it a new int[N][]
, where N
is the length of your list. But, based on the optimizations currently available to the JVM, the zero array is recommended today.