If I have a Collection defined as Collection collection = new ArrayList() which contains String instances, how can I convert it to a String[]? collection.toArray() returns an Object[]. Alternatively, how can I instantiate an ArrayList<String> using reflection?
Note that I cannot hardcode String, the method doing this only knows about the Class that it can work with.
Example:
Object test(Class classToCastTo, Object[] values) {
Collection collection = new ArrayList();
for (Object value : values) {
collection.add(classToCastTo.cast(value));
}
return collection.toArray();
}
If I call this with test(String.class, ...), then it will return an Object[]. How can I make it return a String[]?
Use theCollection.toArray((T[])java.lang.reflect.Array.newInstance(theClass, theCollection.size())), where T is the element type. The cast is safe as long as T is an unparameterized type.
If you have the class, you can write a method like this:
public static <T> T[] arrayBuilder(Class<T> classToCastTo, Collection c) {
return (T[]) c.toArray((T[]) Array.newInstance(classToCastTo, 0));
}
Iterate over the Collection and store it in a String array.
Try this example from my code:
Collection c = new ArrayList();
c.add("Vivek");
c.add("Vishal");
String[] arr = new String[ c.size()];
int j = 0;
for (Object s : c){
arr[j] = (String)s;
j++;
}
The following method is what are you looking for
public <T> T[] test(Class<T> classToCastTo, Object[] values) {
Collection<T> collection = new ArrayList<T>();
for (Object value : values) {
collection.add(classToCastTo.cast(value));
}
return collection.toArray((T[])Array.newInstance(classToCastTo, collection.size()));
}
Based on Ben’s answer, the following code snippet works (compiles without warnings and runs):
private static Object test(Class<?> classToCastTo, Object[] values) {
Collection<Object> collection = new ArrayList<Object>();
for (Object value : values) {
collection.add(classToCastTo.cast(value));
}
return collection.toArray(
(Object[]) java.lang.reflect.Array.newInstance(
classToCastTo, collection.size())
);
}
Now you can call the method via
String[] result = (String[]) test(String.class, someValues);
The trick is casting the array that was created via reflection to Object[] so that it satisfies the static type check and matches the parameter type required by toArray.
That said, I don’t understand why you can’t call the method with a generic parameter. If you don’t have a generic type somewhere, the result of this method will be useless anyway.
This seems to do what you need:
public static void main(String[] args) {
Object[] originalArray = {"abc", "def"};
Class clazz = String.class;
Object[] newArray = test(clazz, originalArray);
System.out.println(newArray.getClass()); //class [Ljava.lang.String;
System.out.println(Arrays.toString(newArray)); //[abc, def]
}
static Object[] test(Class classToCastTo, Object[] values) {
Object[] o = (Object[]) Array.newInstance(classToCastTo, values.length);
System.arraycopy(values, 0, o, 0, values.length);
return o;
}
You will get a java.lang.ArrayStoreException if the original array contains something that is not a String.
If you know your collection only contains Strings, this method
public static <T> T[] toArray(Collection collection, Class<T> clazz) {
T[] array = (T[]) Array.newInstance(clazz, collection.size());
return ((Collection<T>) collection).toArray(array);
}
called as
String[] result = toArray(collection, String.class);
will do what you need, though it will give some warnings about unchecked casts.
If you know your collection can only contain strings though, you ought to be able to declare it as a Collection<String> and avoid this sort of mess.
Related
This question already has answers here:
why does List<String>.toArray() return Object[] and not String[]? how to work around this?
(5 answers)
Closed 3 years ago.
I want to create a static array from a dynamic array of whatever generic type the dynamic array was. I saw List#toArray() which returns Object[] and it doesn't use generics. Is it just safe to cast it to T[] or does the entire array have to be instantiated from the type of class using it?
I went on to try and create my own method in case java didn't provide one but, I got stuck with a compile errors
public static <T> T[] toArray(List<T> list)
{
T[] li = (T[]) Array.newInstance(T.class, list.size());
int index = 0;
for(T obj : list)
{
li[index++] = obj;
}
return li;
}
First of all, you don't need that method. You can use:
List<String> list = new ArrayList<>();
list.add("ff");
list.add("bb");
String[] array = list.toArray (new String[list.size ()]);
In order for your method to work, you have to pass the Class of the generic type parameter:
public static <T> T[] toArray(List<T> list, Class<T> clazz)
{
T[] li = (T[]) Array.newInstance(clazz, list.size());
int index = 0;
for(T obj : list)
{
li[index++] = obj;
}
return li;
}
Then you can call the method with:
String[] array = toArray(list, String.class);
The method proposed by Eran doesn't work if you have a generic element type, because you can't get a Class<List<T>>, say.
Instead, pass an IntFunction<T[]>:
public static <T> T[] toArray(List<? extends T> list, IntFunction<T[]> arraySupplier)
{
T[] li = arraySupplier.get(list.size());
int index = 0;
for(T obj : list)
{
li[index++] = obj;
}
return li;
}
Or, easier, use streams:
return list.stream().toArray(arraySupplier);
Then call like:
String[] array = toArray(list, String[]::new);
List<List<String>> listOfLists = ...
List<?>[] arrayOfLists = toArray(listOfLists, List<?>::new);
Notice that whilst this does support generic array elements, you can only create arrays with a reified element type, so your array type has to be List<?>[]; it still can't be List<String>[].
If your business requirement/Use Case requires an array to be no longer dynamic then you should first create a static array of size equal to your size of dynamic array.
ArrayList<Integer> al = [............] // assuming that ArrayList named al is having some data
int[] arr = new int[al.size()];
// from here you can use a for loop and initialize your static array
for(int i=0; i<arr.length;i++) {
arr[i] = (int) al.get(i); // Unboxing will also be done but still you can type cast to be on safe side
}
// Now you can de-reference the ArrayList object and call garbage collection which will wipe it out of the Heap Memory of your JVM.
al = null; // de-referencing the object by making the reference variable null
System.gc(); // GC happens periodically but to boost performance you can explicitly call it right away.
You can create a method accepting the list of objects and can handle all sorts of arrays using instanceof operator.
I am trying to use java generics-
public T[] toArray()
{
T[] result = (T[])new Object[numberOfEntries];
// some code here to fill the resultarray
return result;
} // end toArray
Now in my main funciton, this is what I am doing-
A<Integer> obj= new A<Integer> ();
obj.add(1); //add method not shown here as it is not relevant to question
obj.add(2);
obj.add(3);
obj.add(4);
Integer arr[] = (Integer[]) obj.toArray();
I get the following error-
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at tester.main(tester.java:14)
How can I fix this?
You are trying to cast an object of type Object[] to Integer[]. These are incompatible types so the JVM throws a ClassCastException. You need to provide some runtime type information to toArray, for example like this:
public T[] toArray(Class<T> clazz)
{
#SuppressWarnings("unchecked")
T[] result = (T[])Array.newInstance(clazz, numberOfEntries);
// some code here to fill the resultarray
return result;
}
...
// and use it like this:
Integer arr[] = obj.toArray(Integer.class);
Due to type erasure, the toArray() method will return an array of type Object[], not Integer[], and it is not possible to cast Object[] to Integer[].
If you want to return an Integer array at runtime from your generic class, you can try passing in the actual class type as a parameter to the toArray() method:
public T[] toArray(Class<T> c, int size) {
#SuppressWarnings("unchecked")
final T[] result = (T[]) Array.newInstance(c, s);
return result;
}
Usage:
A<Integer> obj= new A<Integer> ();
obj.add(1);
obj.add(2);
obj.add(3);
obj.add(4);
Integer[] arr = obj.toArray(Integer.class, obj.size());
Object[] cannot cast to Integer[] directly. Use below code to move elements one by one:
Object[] arr = new Object[SIZE];
Integer[] arr1 = new Integer[arr.length];
int i = 0;
for(Object a:arr){
arr1[i++] = (Integer)a;
}
You have this line in code T[] result = (T[])new Object[numberOfEntries];
You need to replace it with T[] result = (T[])new T[numberOfEntries];
I have the following function
public <T> T getItemsByType(){
T[] retarr = null;
for(int i = 0; i<items.size(); i++){
if(items.get(i).get instanceof T){
}
}
return null;
}
I need my function to search in a list of items for instances of type T and then return an array of T.
Of course, this would've been great if worked but the problem is that:
Cannot perform instanceof check against type parameter T. Use its erasure Object instead since further generic type information will be erased at runtime
What should I do to obtain the same effect?
You need to pass Class<T> clazz as argument to your method, then use it to check if the element can be assignable to that class by using Class#isInstance.
public <T> T getItemsByType(Class<T> clazz) {
T[] retarr = null;
for(int i = 0; i<items.size(); i++){
if(clazz.isInstance(items.get(i))){
//do your logic here...
}
}
return null;
}
Some recomendations:
It would be better returning a List<T> or Collection<T> instead of returning a single T element.
Avoid returning null, so you don't have to do null-defensive programming.
Use an iterator or an enhanced for loop to traverse through all the elements in your list.
So, a better implementation would be:
public <T> List<T> getItemsByType(Class<T> clazz) {
List<T> theList = new ArrayList<T>();
//using Object since you never specified which type of elements holds this list
for (Object o : items) {
if(clazz.isInstance(o)) {
//do your logic here...
theList.add(clazz.cast(o));
}
}
return theList;
}
I am going through Generics Tutorial and was going through example to copy objects from array to collection.
Code
static void fromArrayToCollection(Object[] a, Collection<?> c) {
for (Object o : a) {
c.add(o); // Compile time error
}
}
I am thinking that I can pass object as parameter to collection and it should work without any issues but tutorial says
By now, you will have learned to avoid the beginner's mistake of trying to use Collection as the type of the collection parameter.
Why does it say that passing Object as parameter type to Collection is not correct approach?
Updated:
static void fromArrayToCollection(Object[] a, Collection<Object> c) {
for (Object o : a) {
c.add(o); // Compile time error
}
}
The "beginner mistake" they're referring to is saying Collection<Object> when what you were trying to say is "any Collection/Collection of Anything." It would in the abstract be perfectly legal to declare the method as Collection<Object> it just doesn't meet the design goal of a method that takes in anything.
We want to be able to do this:
public static void main(String[] args) {
String[] stringArray = {"A", "B", "C"};
List<String> stringList = new ArrayList<String>();
fromArrayToCollection(stringArray, stringList);
Integer[] intArray = {1, 2, 3};
List<Integer> intList = new ArrayList<Integer>();
fromArrayToCollection(intArray, intList);
}
public static <T> void fromArrayToCollection(T[] array, Collection<T> collection) {
for (T item: array) {
collection.add(item);
}
}
That wouldn't work if you made it Collection<Object>.
You can't declare the parameter type as Collection<Object> and have it work for multiple types like above because generic types aren't covariant. It would be illegal to say, pass in List<String> to a method with an argument type of Collection<Object>. A Collection<String> is not a Collection<Object>.
Consider the standard example:
List<Integer> intList = new ArrayList<Integer>();
List<Object> objList = intList; //doom impending!!!
objList.add("NOTANUMBER");
int i = intList.get(0).intValue(); //runtime exception!
That's why it's illegal to declare it as Collection<Object> and take a collection of anything.
Your code is equivalent to the following:
static void <T> fromArrayToCollection(Object[] a, Collection<T> c) {
for (Object o : a) {
c.add(o); // Compile time error
}
}
It won't work because there is no restriction on the type of T. For example, you could set T to be Integer, and you are saying that you will add in any instance of Object into a Collection of Integer.
Hope this helps.
What you want is this:
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o); // No compiler error
}
}
You can type the array too. By declaring a typed parameter for the method, you can give that type to each of your inputs.
You could also do this:
static void fromArrayToCollection(Object[] a, Collection<? super Object> c) {
for (Object o : a) {
c.add(o); // No compiler error
}
}
But this seems a little trite.
Following the explanation here, it tells you that you should not rely that a Collection of Something is actually a Collection of Objects, thus they tell you that you should use a Collection of unknow, but you cannot add anything to that collection, you could corrupt it; imagine the following:
static void fromArrayToCollection(Object[] a, Collection<?> c) {
for (Object o : a) {
c.add(o); // Imagine that this will compile
}
}
// somewhere else in the same class
Collection<String> myStrings = new ArrayList<String>();
fromArrayToCollection(someListOfObjects, myStrings);
myStrings.get(0).doSomethingOnlyStringsDo(); // surprise, surprise, i am not a string.
I would like to take a passed List that I know is homogeneous and from it create an array of the same type as the elements within it.
Something like...
List<Object> lst = new ArrayList<Object>;
lst.add(new Integer(3));
/// somewhere else ...
assert(my_array instanceof Integer[]);
The conversion would happen runtime, while the type is lost at compile time. So you should do something like:
public <T> T[] toArray(List<T> list) {
Class clazz = list.get(0).getClass(); // check for size and null before
T[] array = (T[]) java.lang.reflect.Array.newInstance(clazz, list.size());
return list.toArray(array);
}
But beware that the 3rd line above may throw an exception - it's not typesafe.
This method is type safe, and handles some nulls (at least one element must be non-null).
public static Object[] toArray(Collection<?> c)
{
Iterator<?> i = c.iterator();
for (int idx = 0; i.hasNext(); ++idx) {
Object o = i.next();
if (o != null) {
/* Create an array of the type of the first non-null element. */
Class<?> type = o.getClass();
Object[] arr = (Object[]) Array.newInstance(type, c.size());
arr[idx++] = o;
while (i.hasNext()) {
/* Make sure collection is really homogenous with cast() */
arr[idx++] = type.cast(i.next());
}
return arr;
}
}
/* Collection is empty or holds only nulls. */
throw new IllegalArgumentException("Unspecified type.");
}
java.lang.reflect.Array.newInstance(Class<?> componentType, int length)
If you need to dynamically create an array based on a type known only at runtime (say you're doing reflection or generics) you'll probably want Array.newInstance