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.
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.
Related
I'm surprised by how painful it is to use java.util.ArrayList<T>.toArray().
Suppose I declare my array list as:
java.util.ArrayList<double[]> arrayList = new java.util.ArrayList<double[]>();
... add some items ...
Then to convert it to an array, I have to do one of the following:
double[][] array = (double[][])arrayList.toArray(new double[0][]);
or:
double[][] array = (double[][])arrayList.toArray(new double[arrayList.size()][]);
or:
double[][] array = new double[arrayList.size()];
arrayList.toArray(array);
None of the above are very readable. Shouldn't I be able to say the following instead?
double[][] array = arrayList.toArray();
But that gives a compile error because Object[] can't be converted to double[][].
Perhaps it's not possible because toArray has to return Object[]
for backwards compatibility with pre-template days.
But if that's the case, couldn't a friendlier alternative method be added
with a different name? I can't think of a good name, but almost anything
would be better than the existing ways; e.g. the following would be fine:
double[][] array = arrayList.toArrayOfNaturalType();
No such member function exists, but maybe it's possible to write a generic helper function that will do it?
double[][] array = MyToArray(arrayList);
The signature of MyToArray would be something like:
public static <T> T[] MyToArray(java.util.ArrayList<T> arrayList)
Is it possible to implement such a function?
My various attempts at implementing it resulted in compile errors
"error: generic array creation" or "error: cannot select from a type variable".
Here's the closest I was able to get:
public static <T> T[] MyToArray(java.util.ArrayList<T> arrayList, Class type)
{
T[] array = (T[])java.lang.reflect.Array.newInstance(type, arrayList.size());
arrayList.toArray(array);
return array;
}
It's called like this:
double[][] array = MyToArray(arrayList, double[].class);
I wish the redundant final parameter wasn't there, but, even so,
I think this is the least-horrible way that I've seen so far for converting array list to array.
Is it possible to do any better than this?
Is it possible to do any better than this?
Nope.
None of the above are very readable. Shouldn't I be able to say the following instead?
double[][] array = arrayList.toArray();
It would be nice ... but you can't.
The problem is that the toArray() method was specified way back in Java 1.2 with the behavior you are seeing. Generic types were not added to the language until Java 1.5. When they were added, the designers chose the "type erasure" approach, for compatibility with earlier versions of Java. So:
the semantics of the toArray() methods could not be changed without breaking compatibility, and
type erasure makes it impossible for a toArray() method implementation to know what the list's actual element type is, so it could not get it right anyway.
Unfortunately you cannot write
double[][] array = arrayList.toArray();
The reason is that toArray() was defined in JDK 1.2 (prior to generics) to return Object[]. This cannot be changed compatibly.
Generics were introduced in Java 5 but were implemented using erasure. This means that the ArrayList instance has no knowledge at runtime of the types of objects it contains; therefore, it cannot create an array of the desired element type. That's why you have to pass a type token of some sort -- in this case an actual array instance -- to tell ArrayList the type of the array to create.
You should be able to write
double[][] array = arrayList.toArray(new double[0][]);
without a cast. The one-arg overload of toArray() is generified, so you'll get the right return type.
One might think that it's preferable to pass a pre-sized array instead of a throwaway zero-length array. Aleksey Shipilev wrote an article analyzing this question. The answer is, somewhat counterintuitively, that creating a zero-length array is potentially faster.
Briefly, the reason is that allocation is cheap, a zero-length array is small, and it's probably going to be thrown away and garbage collected quickly, which is also cheap. By contrast, creating a pre-sized array requires it to be allocated and then filled with nulls/zeroes. It's then passed to toArray(), which then fills it with values from the list. Thus, every array element is typically written twice. By passing a zero-length array to toArray(), this allows the array allocation to occur in the same code as the array filling code, providing the opportunity for the JIT compiler to bypass the initial zero-fill, since it knows that every array element will be filled.
There is also JDK-8060192 which proposes to add the following:
<A> A[] Collection.toArray(IntFunction<A[]> generator)
This lets you pass a lambda expression that is given the array size and returns a created array of that size. (This is similar to Stream.toArray().) For example,
// NOT YET IMPLEMENTED
double[][] array = arrayList.toArray(n -> new double[n][]);
double[][] array = arrayList.toArray(double[][]::new);
This isn't implemented yet, but I'm still hopeful this can get into JDK 9.
You could rewrite your helper function along these lines:
static <T> T[] myToArray(List<T> list, IntFunction<T[]> generator) {
return list.toArray(generator.apply(list.size()));
}
(Note that there is some subtlety here with concurrent modification of the list, which I'm ignoring for this example.) This would let you write:
double[][] array = myToArray(arrayList, double[][]::new);
which isn't terribly bad. But it's not actually clear that it's any better than just allocating a zero-length array to pass to toArray().
Finally, one might ask why toArray() takes an actual array instance instead of a Class object to denote the desired element type. Joshua Bloch (creator of the Java collections framework) said in comments on JDK-5072831 that this is feasible but that he's not sure it's a good idea, though he could live with it.
There an additional use case here as well, of copying the elements into an existing array, like the old Vector.copyInto() method. The array-bearing toArray(T[]) method also supports this use case. In fact, it's better than Vector.copyInto() because the latter cannot be used safely in the presence of concurrent modification, if the collection's size changes. The auto-sizing behavior of toArray(T[]) handles this, and it also handles the case of creating an array of the caller's desired type as described above. Thus, while adding an overload that takes a Class object would certainly work, it doesn't add much over the existing API.
I've been learning how to program with java and I haven't got any clear explanation about the difference of LinkedList's toArray(T[] a) and toArray() method. The second one simply returns all of the elements within the LinkedList object as an array, right? But, what about the first one?
EDIT :
I mean, I read the documentation from oracle, it says :
Returns an array containing all of the elements in this list in proper
sequence (from first to last element); 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 list 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.)
Like the toArray() method, this method acts as bridge between
array-based and collection-based APIs. Further, this method allows
precise control over the runtime type of the output array, and may,
under certain circumstances, be used to save allocation costs.
I don't understand the meaning of the sentences displayed in bold.
Suppose you've a List<String>, and you want to convert it to String[]. Let's see the working of two methods:
List<String> source = new LinkedList<String>();
// Fill in some data
Object[] array1 = source.toArray();
String[] array2 = source.toArray(new String[source.size()]);
See the difference? The first one simply creates an Object[], because it doesn't know the type of the type parameter <T>, while the second one just fills up the String[] you passed (which is what you want). You would almost always need to use the 2nd method.
There are two differences :
The first returns T[] while the second returns Object[]
The first accepts an array as an argument, and if this array is large enough, it uses this array to store the elements of the Collection, instead of creating a new one.
In order to complete one of my Java assignments, I have to do what seems like the impossible.
I have to create a method that takes in different stuff and plugs it into an array. We don't necessarily know what is being put into the array and thus the array must be able to accept Strings, Double, Integer, etc...
Of course, the obvious solution would be to use ArrayList<E> (i.e. a generic array). However, that's partly the complication of the problem. We cannot use an ArrayList, only a regular array. As far as I can find, when creating an array its intake value must be declared. Which leads me to believe that this assignment is impossible (yet I doubt the teacher would give me an impossible assignment).
Any suggestions?
You can always use an array of Object - Object[].
Object[] objects = new Object[2];
objects[0] = "ABC";
objects[1] = Integer.valueOf("15");
Are you sure you need a generic array or an array that can hold anything?
If the former, then create a class that will act as wrapper of Object[] array and use a <T> generic for type cast when getting the elements of the array, which is similar to the implementation of ArrayList class. If the latter, use Object[] directly.
I'm confused about the design and proper use of toArray(T[]) method in Set (and other collections). If I have a Set of String, why do I need to specify an array of String of size 0, if the method is going to allocate a new String array anyway?
Set<String> stringSet = new Set<String>();
// bla bla bla, insert Strings to set
String[] array = stringSet.toArray(new String[0]);
Is there a better way to just get the array without allocating extra arrays or the explicit type conversion?
The parameter is used to detect the type of the array that's going to be created.
You could argue, why doesn't the JVM use the generic type parameter of the stringSet to detect the type. The answer is that at runtime the generic type parameter is not known due to type erasure, i.e Set<String> becomes Set after compilation.
toArray(new Object[0]) is identical in function to toArray().
If the set 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 set.
If you are using a collection, you may not know the exact number of entries that your set (or any other collection) will have. If you know from the beginning the number of entries, why don't you use an array instead of a collection?
The way I see it, the toArray() method is a simple way to create arrays using a collection for which you don't know a priori how many entries it will have.
Collection.toArray
We use the above method to convert a List<String> object to an equivalent String[].
List<String> foos = new ArrayList<String>();
// foos.toArray(new String[0]);
// foos.toArray(new String[foos.length]);
Which would be the right method to use in order to convert this into an Array.
This one:
foos.toArray(new String[foos.size()]);
Is the correct one to use. If you give toArray an array that is too short to fit the list into, toArray will allocate a new array instead of storing the elements in the array you supply. This means that Java will allocate two arrays instead of one and will waste a few clock cycles this way.
If you see the signature of the both functions you will clearly see whats the difference.
Object[] toArray();
The returned array will be "safe" in that no references to it are maintained by this collection. (In other words, this method must allocate a new array even if this collection is backed by an array). The caller is thus free to modify the returned array.
This method acts as bridge between array-based and collection-based APIs.
<T> T[] toArray(T[] a);
a the array into which the elements of this collection are to be stored, if it is big enough; otherwise, a new array of the same runtime type is allocated for this purpose.
String array[] = foos.toArray(new String[foos.size()]);
Note that it will also work with new String[0] but the array would need to be reallocated if foos.size() > 0.