ChatGPT解决这个技术问题 Extra ChatGPT

Convert ArrayList<String> to String[] array [duplicate]

This question already has answers here: Converting 'ArrayList to 'String[]' in Java (16 answers) Closed 7 years ago.

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 String [] stockArr = (String[]) stock_list.toArray(new String[0]); refer java doc here
@Nishant You do not need to cast! String[] stockArr = stock_list.toArray(new String[0]); is enough.
Have made this answer with an updated approach with JDK-11 introducing a new an equally performant API to toArray(T[]) and similar in syntax to Stream.toArray.

P
Pshemo

Use like this.

List<String> stockList = new ArrayList<String>();
stockList.add("stock1");
stockList.add("stock2");

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

for(String s : stockArr)
    System.out.println(s);

To provide an explanation as to what is going on here, the JVM doesn't know how to blindly downcast Object[] (the result of toArray()) to String[]. To let it know what your desired object type is, you can pass a typed array into toArray(). The typed array can be of any size (new String[1] is valid), but if it is too small then the JVM will resize it on it's own.
@dhackner - "... the JVM doesn't know how to blindly downcast Object[] to String[]". Or more precisely, it is not allowed to do that. If it could do that, it would violate Java type safety.
Use stock_list.toArray(stockArr) instead of stockArr = stock_list.toArray(stockArr). See stackoverflow.com/a/9572820/597657
It may be useful to noobies too that, should your array list contain double, floats, ints, longs (primitive types), you must define your array/arraylist as containing a primitive type object, e.g. Double, Float, Integer, Long. And then from there your normal arrays must be defined with this type, e.g. Double[] myDoubleArray = list.toArray(new Double[listOfDoubles.size()]);
According to Joshua Bloch in Effective Java, preallocating the array harms performance. Provide a zero-length array instead. stockList.toArray(new String[0])
s
st0le

Try this

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

Object[] obj = stock_list.toArray(); String[] stockArr =new String[obj.length]; for(int i=0;i
For anyone wondering what's going on in @QuanNguyen's comment: He is basically retrieving the Object[] by .toArray() and then manually copying the contents over to a new String[], casting each element to a String. It's a bad approach and you should instead just pass the new String[] to .toArray()
You don't need to pass list.size(), this will just create an overhead of a bigger temporary array created that will be discarded right away. I could just be String[] arr = list.toArray(new String[] {});. Still giving +1 due to simplicity of oneliner answer.
@LeoHolanda, If the array provided in the parameter fits the list, the same array is used, if the size doesn't fit, it allocates an array itself, in your case, you end up creating a dummy array (of size 0).
Beware that toArray(new String[0]) is actually faster than toArray(new String[list.size()]): stackoverflow.com/questions/4042434/…
S
Stephen C

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

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.

The latter version uses the zero-length array to determine the type of the result array. (Surprisingly, it is faster to do this than to preallocate ... at least, for recent Java releases. See https://stackoverflow.com/a/4042464/139985 for details.)

From a technical perspective, the reason for this API behavior / 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 to hold the list elements, it is used. Otherwise a new array of the same type and a larger size is allocated and returned as the result.)

1 - In Java, an Object[] is not assignment compatible with a String[]. If it was, then you could do this:

    Object[] objects = new Object[]{new Cat("fluffy")};
    Dog[] dogs = (Dog[]) objects;
    Dog d = dogs[0];     // Huh???

This is clearly nonsense, and that is why array types are not generally assignment compatible.


This doesn't work with primitive types.
If you mean, it worn convert a List<Integer> to an int[] ... that is correct. It converts it to an Integer[].
V
Vitalii Fedorenko

An alternative in Java 8:

String[] strings = list.stream().toArray(String[]::new);

FYI, IntelliJ IDEA suggests to change it as list.toArray(new String[0]). I don't know why.
@JinKwon What is the target language version of your project?
@whirlwin 8, I believe.
Or just: list.toArray(String[]::new);
C
Community

I can see many answers showing how to solve problem, but only Stephen's answer is trying to explain why problem occurs so I will try to add something more on this subject. It is a story about possible reasons why Object[] toArray wasn't changed to T[] toArray where generics ware introduced to Java.

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

In Java, generic type exists at compile-time only. At runtime information about generic type (like in your case <String>) is removed and replaced with Object type (take a look at type erasure). That is why at runtime toArray() have no idea about what precise type to use to create new array, so it uses Object as safest type, because each class extends Object so it can safely store instance of any class.

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

Why? Take a look at this example (lets assume that class B extends A):

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

Although such code will compile, at runtime we will see thrown ClassCastException because instance held by reference a is not actually of type B (or its subtypes). Why is this problem (why this exception needs to be cast)? One of the reasons is that B could have new methods/fields which A doesn't, so it is possible that someone will try to use these new members via b reference even if held instance doesn't have (doesn't support) them. In other words we could end up trying to use data which doesn't exist, which could lead to many problems. So to prevent such situation JVM throws exception, and stop further potentially dangerous code.

You could ask now "So why aren't we stopped even earlier? Why code involving such casting is even compilable? Shouldn't compiler stop it?". Answer is: no because compiler can't know for sure what is the actual type of instance held by a reference, and there is a chance that it will hold instance of class B which will support interface of b reference. Take a look at this example:

A a = new B(); 
      //  ^------ Here reference "a" holds instance of type B
B b = (B)a;    // so now casting is safe, now JVM is sure that `b` reference can 
               // safely access all members of B class

Now lets go back to your arrays. As you see in question, we can't cast instance of Object[] array to more precise type String[] like

Object[] arr = new Object[] { "ab", "cd" };
String[] arr2 = (String[]) arr;//ClassCastException will be thrown

Here problem is a little different. Now we are sure that String[] array will not have additional fields or methods because every array support only:

[] operator,

length filed,

methods inherited from Object supertype,

So it is not arrays interface which is making it impossible. Problem is that Object[] array beside Strings can store any objects (for instance Integers) so it is possible that one beautiful day we will end up with trying to invoke method like strArray[i].substring(1,3) on instance of Integer which doesn't have such method.

So to make sure that this situation will never happen, in Java array references can hold only

instances of array of same type as reference (reference String[] strArr can hold String[])

instances of array of subtype (Object[] can hold String[] because String is subtype of Object),

but can't hold

array of supertype of type of array from reference (String[] can't hold Object[])

array of type which is not related to type from reference (Integer[] can't hold String[])

In other words something like this is OK

Object[] arr = new String[] { "ab", "cd" }; //OK - because
               //  ^^^^^^^^                  `arr` holds array of subtype of Object (String)
String[] arr2 = (String[]) arr; //OK - `arr2` reference will hold same array of same type as 
                                //     reference

You could say that one way to resolve this problem is to find at runtime most common type between all list elements and create array of that type, but this wont work in situations where all elements of list will be of one type derived from generic one. 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()

A[] arr = elements.toArray();

would return array of B class new B[]. Problem with this array is that while compiler would allow you to edit its content by adding new A() element to it, you would get ArrayStoreException because B[] array can hold only elements of class B or its subclass, to make sure that all elements will support interface of B, but instance of A may not have all methods/fields of B. So this solution is not perfect.

Best 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.

This is all correct, but the root cause of the problem is deeper / older than generic typing. The toArray method has behaved like this since the collection framework was introduced. It predates generic by a number of years. (You could say, generics didn't fix the problem ...)
@StephenC True, I focused only at generics problem forgetting that this tool was not always part of collection framework. I will try to rewrite this answer to include your information, but can't do it not. If you have time feel free to edit it. Also feel free to down-vote it if you think I doesn't belong to this question :)
R
Rick Hanlon II

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[] 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?


Object[] obj = stock_list.toArray();
@QuanNguyen: What are you saying? What point are you trying to make?
toArray(new String[0]) is faster: shipilev.net/blog/2016/arrays-wisdom-ancients