I am trying to convert a Set to an Array.
Set<String> s = new HashSet<String>(Arrays.asList("mango","guava","apple"));
String[] a = s.toArray(new String[0]);
for(String x:a)
System.out.println(x);
And it works fine. But I don't understand the significance of new String[0] in String[] a = s.toArray(new String[0]);.
I mean initially I was trying String[] a = c.toArray();, but it wan't working. Why is the need for new String[0].
It is the array into which the elements of the Set are to be stored, if it is big enough; otherwise, a new array of the same runtime type is allocated for this purpose.
Object[] toArray(), returns an Object[] which cannot be cast to String[] or any other type array.
T[] toArray(T[] a) , returns an array containing all of the elements in this set; the runtime type of the returned array is that of the specified array. 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 go through the implementing code (I'm posting the code from OpenJDK) , it will be clear for you :
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
The parameter is a result of one of the many well-known limitations in the Java generics system. Basically, the parameter is needed in order to be able to return an array of the correct type.
Related
I recently had a problem typecasting/converting an ArrayList of arrays to a 2d array. I found an answer on this site that told me to use
List<Object[]> arrayList;
// initialize and fill the list of arrays
// all arrays have the same length
Object[][] array2d = arrayList.toArray(new Object[][] {});
That worked, but I googled it for a bit and read somewhere that it is conseidered bad practice. I still used it, as it was the only one line variant that actually worked.
What does it actually mean and why is it considered bad? I don't understand the [][]{} bit. I'm guessing that you pass an empty but initialized Object[][] to the .toArray() method?
The only thing I see is that it creates an array but will not use it.
One the Javadoc you can see:
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.
And it can be verified on the method source code:
#SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
So what you are creating is an empty array, then the method will create another one.
Object[][] array2d = arrayList.toArray(new Object[arrayList.size()][]);
Concerning the [][]{} your guess is correct.
Some tips
here
I can't find any reason for your example be considered bad practice!
When I was going through ArrayList implementation, I found a weird piece of code in toArray(T[]) method.
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
The part is,
if (a.length > size)
a[size] = null;
why only the element at this index in the array is set to null? Once the array is filled with the contents of the list, the elements at the remaining indices should have been set to null, right? Or am I missing something here?
The javadoc explains why:
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.)
I have written a custom RequestWrapper to add cookies in to the request
#Override
public Cookie[] getCookies()
{
Cookie c=new Cookie("iPlanetDirectoryPro", "macleanpinto");
Cookie c1=new Cookie("iPlanetDirectoryPro1", "macleanpinto1");
Collection<Cookie> result = new ArrayList<Cookie>(2);
result.add(c);
result.add(c1);
return (Cookie[]) result.toArray();
}
The above code at Return throws java.lang.ClassCastException. I am not able to be figure out why it does. Since i have created collection of cookies and i am trying return array list of cookies.
Thanks
can you try result.toArray(new Cookie[2]); instead of (Cookie[]) result.toArray();?
Reason being - calling just result.toArray(); returns Object[]
If you see the source code of ArrayList
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
and
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
here the latter, clearly uses Arrays.copyOf passing the type of array
And Object[] can't be cast to Cookie[]
Try using ArrayList.toArray(T[] a).
Example:
return result.toArray(new Cookie[result.size()]);
As stated in the API, ArrayList.toArray() returns an array of type Object. Here's a thread explaining why you can't cast Object[] to String[]. Even though you're using Cookie[] the same principle applies.
ArrayList internally uses an Object[] to store the elements. So, when you use ArrayList#toArray() method, it returns the Object[].
Now, you cannot cast from an Object[] to a Cookie[], b'coz Object is not a subtype, but a supertype of Cookie. For example, consider below code:
Object[] objArray = {"a", 1, new Date()}; // an Object[]
Cookie[] cookieArray = (Cookie[]) objArray; // You don't expect this to run
So, if you want an Cookie[], you have to iterate over the Object[] and manually convert each element to Cookie type, and add it to a new array. That is what ArrayList#toArray(T[]) does.
So, you can change your return statement to:
return result.toArray(new Cookie[result.size()]);
I've taken result.size() to avoid creation of an extra array in the method. You could have passed new Cookie[0] too. But, a new array will be created inside the toArray method.
Why does Collection<E>.toArray() (non-parameterized method) return Object[]?
Is it one of those consciously taken decisions? Is there any reason why the toArray() method would not be able to return a E[], if it wanted to?
It's because an array of type T cannot be instantiated without knowing the type Class<T>. Contrast this with toArray(T[] array), which has the following source (example from LinkedList). Notice that the passed-in array is used not only as a possible container, but to possibly instantiate a new array of that type. This code throws an exception if T is not a superclass of E; if objects can't be added to the array.
#SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
if (a.length > size)
a[size] = null;
return a;
}
Generics in Java are implemented using a technique called type erasure. This means that an instance of a generic class does not have any information about its generic type. Consider this
List<String> list = new ArrayList<String>();
list.toArray();
since list does not know about its generic type it can create only Object[]
Array containers have an associated item data type, preserved at runtime. If you construct an Object array and then add strings, this object won't be castable to String[]:
Object[] objArr = new Object[] {"a", "b"};
String[] strArr = (String[]) objArr; //Produces ClassCastException
You can also notice how this array property is used at runtime when you add items of an incorrect type to an array:
String[] strArr = new String[] {"a", "b"};
Object[] objArr = (Object[]) strArr; //Legal this time
objArr[0] = 15; //Produces ArrayStoreException
Generic type arguments are erased at runtime, so the JVM doesn't know what specific array type to create at runtime when you call toArray().
In the Java collections framework, the Collection interface declares the following method:
<T> T[] toArray(T[] a)
Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array. If the collection 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 collection.
If you wanted to implement this method, how would you create an array of the type of a, known only at runtime?
Use the static method
java.lang.reflect.Array.newInstance(Class<?> componentType, int length)
A tutorial on its use can be found here:
http://java.sun.com/docs/books/tutorial/reflect/special/arrayInstance.html
By looking at how ArrayList does it:
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
Array.newInstance(Class componentType, int length)
To create a new array of a generic type (which is only known at runtime), you have to create an array of Objects and simply cast it to the generic type and then use it as such. This is a limitation of the generics implementation of Java (erasure).
T[] newArray = (T[]) new Object[X]; // where X is the number of elements you want.
The function then takes the array given (a) and uses it (checking it's size beforehand) or creates a new one.