dimanche 18 mai 2014

Java - Convert ArrayList <String>à String [] - Stack Overflow


I'm working in the android environment and have tried the following code, but it doesn't seem to be working.


String [] stockArr = (String[]) stock_list.toArray();

If I define as follows:


String [] stockArr = {"hello", "world"};

it works. Is there something that I'm missing?




Use like this.


ArrayList<String> stock_list = new ArrayList<String>();
stock_list.add("stock1");
stock_list.add("stock2");
String[] stockArr = new String[stock_list.size()];
stockArr = stock_list.toArray(stockArr);
for(String s : stockArr)
System.out.println(s);



Try this


String[] arr = list.toArray(new String[list.size()]);



What is happening is that stock_list.toArray() is creating an Object[] rather than a String[] and hence the typecast is failing.


The correct code would be:


  String [] stockArr = stockList.toArray(new String[stockList.size()]);

or even


  String [] stockArr = stockList.toArray(new String[0]);

For more details, refer to the javadocs for the two overloads of List.toArray.


(From a technical perspective, the reason for this API behaviour / design is that an implementation of the List<T>.toArray() method has no information of what the <T> is at runtime. All it knows is that the raw element type is Object. By contrast, in the other case, the array parameter gives the base type of the array. (If the supplied array is big enough, it is used. Otherwise a new array of the same type and a larger size is allocated and returned as the result.)




I can see many answers that show how to solve problem, but only Stephen C is trying to point why problem occurs so will try to say something more on this subject


Why String [] stockArr = (String[]) stock_list.toArray(); wont work?


In Java generics work in compile time. In runtime informations about generic type (like in your case <String>) are removed and replaced with Object type (take a look at type erasure). That is why toArray() have no idea about what type to use to create new array and uses Object as common type.


Now the problem is that you can't cast instance of Object[] to String[].


Why? Take a look at this example:


//B extends A
A a = new A();
B b = (B)a;

Although it will compile it will throw ClassCastException because instance of A class cant be cast to B class. Why is this not allowed? One of reason is: what if B had methods/fields that A doesn't and someone woild like to use them via b reference? Would that be safe? No. This would make application crash.


Only way to be able to cast object from A a reference to B b reference is when under a is instance of B class like in this example


A a = new B(); // Here under a reference is stored object of type B
B b = (B)a; // so now casting is safe

Here we know that object is in fact instance of B class so casting it to its type is safe.


So back to your example you cant expect


Object[] arr = new Object[] { "ab", "cd" };
String[] arr2 = (String[]) arr;

to cast correctly from object of type Object[] to more precise type String[], but you would be able to do this in situation like this


Object[] arr = new String[] { "ab", "cd" };
String[] arr2 = (String[]) arr;

because now we are trying to cast new String[] to its String[] type.




You could say that one way to resolve this problem is to check most common type of all list elements and create array of that type, but this wont work in situations where all elements of list will be derived from generic type. Take a look


//B extends A
List<A> elements = new ArrayList<A>();
elements.add(new B());
elements.add(new B());

now most common type is B, not A so toArray() would return array of B class like new B[] and such array cant be cast to A[] because it could lead to trying to add elements that extend A but not B to this array.




So only solution to this problem is explicitly tell what type of array toArray() should be returned by passing this type as method argument like


String[] arr = list.toArray(new String[list.size()]);

or


String[] arr = list.toArray(new String[0]); //if size of array is smaller then list it will be automatically adjusted.



The correct way to do this is:


String[] stockArr = stock_list.toArray(new String[stock_list.size()]);

I'd like to add to the other great answers here and explain how you could have used the Javadocs to answer your question.


The Javadoc for toArray() (no arguments) is here. As you can see, this method returns an Object[] and not String[] which is an array of the runtime type of your list:



public Object[] toArray()


Returns an array containing all of the elements in this collection. If the collection makes any guarantees as to what order its elements are returned by its iterator, this method must return the elements in the same order. The returned array will be "safe" in that no references to it are maintained by the collection. (In other words, this method must allocate a new array even if the collection is backed by an Array). The caller is thus free to modify the returned array.



Right below that method, though, is the Javadoc for toArray(T[] a). As you can see, this method returns a T[] where T is the type of the array you pass in. At first this seems like what you're looking for, but it's unclear exactly why you're passing in an array (are you adding to it, using it for just the type, etc). The documentation makes it clear that the purpose of the passed array is essentially to define the type of array to return (which is exactly your use case):



public <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 the collection fits in the specified array with room to spare (i.e., the array has more elements than the collection), 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 collection only if the caller knows that the collection does not contain any null elements.)


If this collection makes any guarantees as to what order its elements are returned by its iterator, this method must return the elements in the same order.


This implementation checks if the array is large enough to contain the collection; if not, it allocates a new array of the correct size and type (using reflection). Then, it iterates over the collection, storing each object reference in the next consecutive element of the array, starting with element 0. If the array is larger than the collection, a null is stored in the first location after the end of the collection.



Of course, an understanding of generics (as described in the other answers) is required to really understand the difference between these two methods. Nevertheless, if you first go to the Javadocs, you will usually find your answer and then see for yourself what else you need to learn (if you really do).


Also note that reading the Javadocs here helps you to understand what the structure of the array you pass in should be. Though it may not really practically matter, you should not pass in an empty array like this:


String [] stockArr = stockList.toArray(new String[0]);  

Because, from the doc, this implementation checks if the array is large enough to contain the collection; if not, it allocates a new array of the correct size and type (using reflection). There's no need for the extra overhead in creating a new array when you could easily pass in the size.


As is usually the case, the Javadocs provide you with a wealth of information and direction.


Hey wait a minute, what's reflection?



I'm working in the android environment and have tried the following code, but it doesn't seem to be working.


String [] stockArr = (String[]) stock_list.toArray();

If I define as follows:


String [] stockArr = {"hello", "world"};

it works. Is there something that I'm missing?



Use like this.


ArrayList<String> stock_list = new ArrayList<String>();
stock_list.add("stock1");
stock_list.add("stock2");
String[] stockArr = new String[stock_list.size()];
stockArr = stock_list.toArray(stockArr);
for(String s : stockArr)
System.out.println(s);


Try this


String[] arr = list.toArray(new String[list.size()]);


What is happening is that stock_list.toArray() is creating an Object[] rather than a String[] and hence the typecast is failing.


The correct code would be:


  String [] stockArr = stockList.toArray(new String[stockList.size()]);

or even


  String [] stockArr = stockList.toArray(new String[0]);

For more details, refer to the javadocs for the two overloads of List.toArray.


(From a technical perspective, the reason for this API behaviour / design is that an implementation of the List<T>.toArray() method has no information of what the <T> is at runtime. All it knows is that the raw element type is Object. By contrast, in the other case, the array parameter gives the base type of the array. (If the supplied array is big enough, it is used. Otherwise a new array of the same type and a larger size is allocated and returned as the result.)



I can see many answers that show how to solve problem, but only Stephen C is trying to point why problem occurs so will try to say something more on this subject


Why String [] stockArr = (String[]) stock_list.toArray(); wont work?


In Java generics work in compile time. In runtime informations about generic type (like in your case <String>) are removed and replaced with Object type (take a look at type erasure). That is why toArray() have no idea about what type to use to create new array and uses Object as common type.


Now the problem is that you can't cast instance of Object[] to String[].


Why? Take a look at this example:


//B extends A
A a = new A();
B b = (B)a;

Although it will compile it will throw ClassCastException because instance of A class cant be cast to B class. Why is this not allowed? One of reason is: what if B had methods/fields that A doesn't and someone woild like to use them via b reference? Would that be safe? No. This would make application crash.


Only way to be able to cast object from A a reference to B b reference is when under a is instance of B class like in this example


A a = new B(); // Here under a reference is stored object of type B
B b = (B)a; // so now casting is safe

Here we know that object is in fact instance of B class so casting it to its type is safe.


So back to your example you cant expect


Object[] arr = new Object[] { "ab", "cd" };
String[] arr2 = (String[]) arr;

to cast correctly from object of type Object[] to more precise type String[], but you would be able to do this in situation like this


Object[] arr = new String[] { "ab", "cd" };
String[] arr2 = (String[]) arr;

because now we are trying to cast new String[] to its String[] type.




You could say that one way to resolve this problem is to check most common type of all list elements and create array of that type, but this wont work in situations where all elements of list will be derived from generic type. Take a look


//B extends A
List<A> elements = new ArrayList<A>();
elements.add(new B());
elements.add(new B());

now most common type is B, not A so toArray() would return array of B class like new B[] and such array cant be cast to A[] because it could lead to trying to add elements that extend A but not B to this array.




So only solution to this problem is explicitly tell what type of array toArray() should be returned by passing this type as method argument like


String[] arr = list.toArray(new String[list.size()]);

or


String[] arr = list.toArray(new String[0]); //if size of array is smaller then list it will be automatically adjusted.


The correct way to do this is:


String[] stockArr = stock_list.toArray(new String[stock_list.size()]);

I'd like to add to the other great answers here and explain how you could have used the Javadocs to answer your question.


The Javadoc for toArray() (no arguments) is here. As you can see, this method returns an Object[] and not String[] which is an array of the runtime type of your list:



public Object[] toArray()


Returns an array containing all of the elements in this collection. If the collection makes any guarantees as to what order its elements are returned by its iterator, this method must return the elements in the same order. The returned array will be "safe" in that no references to it are maintained by the collection. (In other words, this method must allocate a new array even if the collection is backed by an Array). The caller is thus free to modify the returned array.



Right below that method, though, is the Javadoc for toArray(T[] a). As you can see, this method returns a T[] where T is the type of the array you pass in. At first this seems like what you're looking for, but it's unclear exactly why you're passing in an array (are you adding to it, using it for just the type, etc). The documentation makes it clear that the purpose of the passed array is essentially to define the type of array to return (which is exactly your use case):



public <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 the collection fits in the specified array with room to spare (i.e., the array has more elements than the collection), 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 collection only if the caller knows that the collection does not contain any null elements.)


If this collection makes any guarantees as to what order its elements are returned by its iterator, this method must return the elements in the same order.


This implementation checks if the array is large enough to contain the collection; if not, it allocates a new array of the correct size and type (using reflection). Then, it iterates over the collection, storing each object reference in the next consecutive element of the array, starting with element 0. If the array is larger than the collection, a null is stored in the first location after the end of the collection.



Of course, an understanding of generics (as described in the other answers) is required to really understand the difference between these two methods. Nevertheless, if you first go to the Javadocs, you will usually find your answer and then see for yourself what else you need to learn (if you really do).


Also note that reading the Javadocs here helps you to understand what the structure of the array you pass in should be. Though it may not really practically matter, you should not pass in an empty array like this:


String [] stockArr = stockList.toArray(new String[0]);  

Because, from the doc, this implementation checks if the array is large enough to contain the collection; if not, it allocates a new array of the correct size and type (using reflection). There's no need for the extra overhead in creating a new array when you could easily pass in the size.


As is usually the case, the Javadocs provide you with a wealth of information and direction.


Hey wait a minute, what's reflection?


0 commentaires:

Enregistrer un commentaire