I tested this code:
Collection l = new ArrayList<Object>();
l.add(5);
l.add("test");
l.add(6);
Integer[] arr= l.toArray(new Integer[2]);
I tried to get only Integers from this collection and got this exception:
Exception in thread "main" java.lang.ArrayStoreException
at java.lang.System.arraycopy(Native Method)
at java.util.Arrays.copyOf(Arrays.java:2248)
at java.util.ArrayList.toArray(ArrayList.java:389)
at c.Main.main(Main.java:15)
May be there is an other way to filter but I need to understand the toArray(Object[] a) method. Why can't I filter by telling type and size of the array?
Why i can't filter by telling type and size of the array?
Because that's not what toArray() does, and nowhere does it say it can be used as a filter.
Functions & API's have definitions for what they do (and sometimes how they do that). You can't expect a function to do what you want it to do if it's not designed to do that.
toArray attempts to store the entire collection in the given array. If you want to filter it, you'll have to do so yourself. E.g.:
Integer[] arr =
l.stream().filter(x -> x instanceof Integer).toArray(Integer[]::new);
Please look at the java doc of the method. Each method works as per the contract it declares to the outside world (as per the signature or java doc).
<T> T[] toArray(T[] a); Clearly says in its java doc the below statement.
Throws:ArrayStoreException - if the runtime type of the specified array is not a supertype of the runtime type of every element in this collection.
In your case type of each of your element is not Integer.
Related
As I am new to Java, I have few doubts regarding Java generics behavior. Here I have implemented own List functionality using native list as wrapper. Below is the code,
ListWrapper<String> li = new ListWrapper<String>(); // wrapper for native list implemented
li.add(“Hello”);
Iterated the elements in the list by converting the list items to array and used in foreach loop.
//Method which used to convert as array in ListWrapper.
public T[] toArray() {
return (T[]) _list.toArray(); // _list is the native list (i.e) java.util.List which converts list items to object[] array.
}
On using this method in for-each loop faced class-cast exception only on compiled class(.jar) but works properly on attaching the source.
for(Object s : li.toArray()) { // Class cast exception occurred here.
System.out.println(s);
}
My understanding:
toArray() in the for-each loop will be called once and iterator will be called implicitly to iterate over the elements. As I have casted the toArray() as T[] (here T will be of String), hence Iterator will try to access the object[] array. So, class cast exception of [Ljava.lang.Object; cannot be cast to [Ljava.lang.String occurred. Please correct me if am wrong.
Also, I would like to know why the same code works properly while running by attaching the source. Can anyone clarify for me?
In Java, an array object knows its component type at runtime, and so when creating an array, you need to provide the component type at runtime. On the other hand, an instance of a generic class does not know its component type at runtime. For a List<T> instance, it doesn't by default know what T is at runtime, so there is no way it can create an array with a runtime component type of T.
List's toArray() method with no arguments will always create an array with a runtime component type of Object. This array will always have the runtime class Object[]; it cannot be String[] or Integer[] or whatever. A variable of type String[] cannot point to this object, since Object[] is not a subtype of String[], so it will cause a ClassCastException. (And by the way, all this was true before there was generics; generics improved some of the signatures, but the runtime behavior is the same.) Your cast to T[] is fundamentally unsafe.
When you do unsafe casts in generics, it can cause ClassCastExceptions in other places where there isn't an explicit cast. It will not cause a ClassCastException where you cast to T[], because T is erased to Object (T's upper bound), so the erased code is just casting Object[] to Object[]. However, the fact that the method returns T[] means that the caller of the method, if they have a specific type for T, can cast the return result. So here, the caller expects li.toArray() to return type String[] (since li has type ListWrapper<String>, so T is String, and ListWrapper.toArray() returns T[]). So it can insert a cast of the result to String[], even though you don't see a cast in the code. That is why you get a ClassCastException there.
(Now, you could argue that technically it doesn't need to cast to String[], since it's used in a foreach loop, and each element is assigned to a variable of type Object, so it is valid to leave li.toArray() as Object[] and not cast. But it is also permissible for the compiler to cast it anyway; and here, the compiler decided to insert a cast.)
List has another toArray() method, which takes one argument of array type. This method will either use the passed array object to store the results, or extract the runtime component type out of the passed array object, and create a new array object with the same runtime component type. So the returned array object will have the same runtime class as the passed array object. So you can use this toArray() method to get an array object whose runtime class is a more specific array type than Object[], but you will have to give an array object of the right type at runtime.
If you want to implement a type-safe .toArray() method in your ListWrapper class, you would have to do something like what List does -- either it must always return an array with runtime class Object[], and therefore it must be returned in type Object[]; and/or it must take in an array object as an argument to extract the runtime component type from, to create an array object of the right component type.
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.
This one list object is biting me in the butt..
Any time I try to add an element to it, it produces this:
Caused by: java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
The line producing the error is insignificant, but here it is anyways:
AdventureLobbies.players.add(args[0].toLowerCase());
Should I not be accessing it statically?
Actual declaration of variable:
AdventureLobbies.players = Arrays.asList(rs.getString("players").toLowerCase().split(","));
Any ideas? Can't find anything on Google that's worthwhile.
Arrays.asList() will give you back an unmodifiable list, and that is why your add is failing. Try creating the list with:
AdventureLobbies.players = new ArrayList(Arrays.asList(rs.getString("players").toLowerCase().split(",")));
The java docs say
asList
#SafeVarargs
public static <T> List<T> asList(T... a)
"Returns a fixed-size list backed by the specified array"
Your list is fixed size, meaning it cannot grow or shrink and so when you call add, it throws an unsupported operation exception
This exception is very familiar with accessing objects that will not permit the access according to java language rules like accessing immutable objects, for that reason instantiate it in the following way instead:
AdventureLobbies.players = new ArrayList(Arrays.
asList(rs.getString("players").toLowerCase().split(","))); // Perfectly done
This question already has answers here:
Converting 'ArrayList<String> to 'String[]' in Java
(17 answers)
Closed 8 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 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);
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 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.
An alternative in Java 8:
String[] strings = list.stream().toArray(String[]::new);
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.
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?
People say that asList method convert the array into list and its not copying, so every change in 'aList' will reflect into 'a'. So add new values in 'aList' is illegal, since array have fixed size.
But, asList() method returns ArrayList<T>. How the compiler differentiates line 3 from 5. Line 3 gives me exception (UnsupportedOperationException).
String[] a = {"a","b","c","d"};//1
List<String> aList = Arrays.asList(a);//2
aList.add("e");//3
List<String> b = new ArrayList<String>();//4
b.add("a");//5
This List implementation you receive from Arrays.asList is a special view on the array - you can't change it's size.
The return type of Arrays.asList() is java.util.Arrays.ArrayList which is often confused with java.util.ArrayList. Arrays.ArrayList simply shows the array as a list.
Read again, the type of Arrays.asList is:
public static <T> List<T> asList(T... a)
which clearly states that asList returns an object that implements interface java.util.List, nowhere does it says it will return an instance of class java.util.ArrayList.
Next, notice that the documentation on List.add says:
boolean add(E e)
Appends the specified element to the end of this list (optional operation).
Technically, everytime you use a variable typed as List (instead of ArrayList), you should always be careful to expect that this method may throw UnsupportedOperationException. If you are sure that you will only receive a List implementation that always have the correct semantic of .add(), then you can omit the check at the risk of a bug when your assumption is invalidated.
Manoj,
The Return type of Arrays.List is some unknown internal implementation of the List interface and not java.util.ArrayList, so you can assign it only to a List type.
If you assign it to an ArrayList for instance it will give you compile time error
"Type mismatch: cannot convert from List to ArrayList"
ArrayList<String> aList = Arrays.asList(a);// gives Compile time error
From the Javadoc "Arrays.asList Returns a fixed-size list backed by the specified array. (Changes to the returned list "write through" to the array.) " that means that you are only provided a list view of the Array which IMO is created at runtime and ofcourse you cannot change the size of an array so you can't change size of "Arrays.asList" also.
IMO the internal implementation of Arrays.asList has all the implemented methods which can change the size of the Array as -
void add(E e)
{
//some unknown code
throw(java.lang.UnsupportedOperationException);
}
so whenever you attempt to alter the size of the Array it throws the UnsupportedOperationException.
Still if you want to add some new items to an ArrayList by using such a syntax, you can do so by creating a subclass of Arraylist(preferably by using anonymous subclass of ArrayList). You can pass the return type of Arrays.List to the constructor of ArrayList, (ie. public ArrayList(Collection c)) something like this -
List<String> girlFriends = new java.util.ArrayList<String>(Arrays.asList("Rose", "Leena", "Kim", "Tina"));
girlFriends.add("Sarah");
Now you can easily add Sarah to your GF list using the same syntax.
PS - Please select this one or another one as your answer because evrything has been explained. Your low Acceptance rate is very discouraging.
asList() doesn't return a java.util.ArrayList, it returns a java.util.Arrays$ArrayList. This class doesn't even extend java.util.ArrayList, so its behaviour can be (and is) completely different.
The add() method is inherited from java.util.AbstractList, which by default just throws UnsupportedOperationException.
You're assuming that Arrays.asList() returns an ArrayList, but that's not the case. Arrays.asList() returns an unspecified List implementation. That implementaton simply throws an UnsupportedOperationException on each unsupported method.
It's an exception and not a compiler error. It is thrown when the program is run and not at the compile time. Basically the actual class that Arrays.asList will return has a throw UnsupporteOperationException inside the add() method.
To be more specific Arrays.asList will return an inner class defined inside the Arrays class that is derived from AbstractList and does not implement the add method. The add method from the AbstractList is actually throwing the exception.
The key to this is the List implementation returned by
List<String> aList = Arrays.asList(a);
If you look at the source code in Arrays you will see that it contains an internal private static class ArrayList. This is not the same as java.util.ArrayList.
asList returns a fixed-size list, so that you cannot add new elements to it. Because the list it returns is really a "view" of the array it was created from ('a' in your case), it makes sense that you won't be able to add elements - just like you can't add elements to an array. See the docs for asList