Convert a generic list to an array

JavaGenerics

Java Problem Overview


I have searched for this, but unfortunately, I don't get the correct answer.

class Helper {
    public static <T> T[] toArray(List<T> list) {
        T[] array = (T[]) new Object[list.size()];
        for (int i = 0; i < list.size(); i++) {
            array[i] = list.get(i);
        }
        return array;
    }
}

Test it:

public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("abc");
    String[] array = toArray(list);
    System.out.println(array);
}

But there is an error thrown:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at test.Helper.main(Helper.java:30)

How to solve this?


UPDATE

I want this method, because sometimes, the type in my code is too long:

newEntries.toArray(new IClasspathEntry[0])

I'd hope to call:

toArray(newEntries)

FINALLY

It seems impossible to create such a method, thank you all very much!

Java Solutions


Solution 1 - Java

This is due to type erasure. The generics are removed in compilation, thus the Helper.toArray will be compiled into returning an Object[].

For this particular case, I suggest you use List.toArray(T[]).

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

Solution 2 - Java

You can just call list.toArray(T[] array) and not have to worry about implementing it yourself, but as aioobe said, you can't create an array of a generic type due to type erasure. If you need that type back, you need to create a typed instance yourself and pass it in.

Solution 3 - Java

If you want to produce your method through brute force, and you can guarantee that you'll only call the method with certain restrictions, you can use reflection:

public static <T> T[] toArray(List<T> list) {
    T[] toR = (T[]) java.lang.reflect.Array.newInstance(list.get(0)
                                           .getClass(), list.size());
	for (int i = 0; i < list.size(); i++) {
        toR[i] = list.get(i);
    }
    return toR;
}

This approach has problems. As list can store subtypes of T, treating the first element of the list as the representative type will produce a casting exception if your first element is a subtype. This means that T can't be an interface. Also, if your list is empty, you'll get an index out of bounds exception.

This should only be used if you only plan to call the method where the first element of the list matches the Generic type of the list. Using the provided toArray method is much more robust, as the argument provided tells what type of array you want returned.

Solution 4 - Java

You can't instantiate a Generic type like you did here:

 T[] array = (T[]) new Object[list.size()];

As, if T is bounded to a type, you're typecasting the new Object array to a bounded type T. I would suggest using List.toArray(T[]) method instead.

Solution 5 - Java

See Guava's Iterables.toArray(list, class).

Example:

@Test
public void arrayTest() {
    List<String> source = Arrays.asList("foo", "bar");
    String[] target = Iterables.toArray(source, String.class);
}

Solution 6 - Java

public static <T> T[] toArray(Collection<T> c, T[] a) {
	return c.size()>a.length ?
		c.toArray((T[])Array.newInstance(a.getClass().getComponentType(), c.size())) :
		c.toArray(a);
}

/** The collection CAN be empty */
public static <T> T[] toArray(Collection<T> c, Class klass) {
	return toArray(c, (T[])Array.newInstance(klass, c.size()));
}

/** The collection CANNOT be empty! */
public static <T> T[] toArray(Collection<T> c) {
	return toArray(c, c.iterator().next().getClass());
}

Solution 7 - Java

String[] array = list.toArray(new String[0]);

Solution 8 - Java

The problem is the component type of the array that is not String.

Also, it would be better to not provide an empty array such as new IClasspathEntry[0]. I think it is better to gives an array with the correct length (otherwise a new one will be created by List#toArray which is a waste of performance).

Because of type erasure, a solution is to give the component type of the array.

Example:

public static <C, T extends C> C[] toArray(Class<C> componentType, List<T> list) {
    @SuppressWarnings("unchecked")
    C[] array = (C[])Array.newInstance(componentType, list.size());
    return list.toArray(array);
}

The type C in this implementation is to allow creation of an array with a component type that is a super type of the list element types.

Usage:

public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("abc");

    // String[] array = list.toArray(new String[list.size()]); // Usual version
    String[] array = toArray(String.class, list); // Short version
    System.out.println(array);

    CharSequence[] seqArray = toArray(CharSequence.class, list);
    System.out.println(seqArray);

    Integer[] seqArray = toArray(Integer.class, list); // DO NOT COMPILE, NICE !
}

Waiting for reified generics..

Solution 9 - Java

As pointed earlier this will work:

String[] array = list.toArray(new String[0]);

And this will also work:

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

However, in the first case a new array will be generated. You can see how this is implemented in Android:

@Override public <T> T[] toArray(T[] contents) {
    int s = size;
    if (contents.length < s) {
        @SuppressWarnings("unchecked") T[] newArray
            = (T[]) Array.newInstance(contents.getClass().getComponentType(), s);
        contents = newArray;
    }
    System.arraycopy(this.array, 0, contents, 0, s);
    if (contents.length > s) {
        contents[s] = null;
    }
    return contents;
}

Solution 10 - Java

Worked solution!

Just copy interface and class inside your project. This :

public interface LayerDataTransformer<F, T> {
    T transform(F from);

    Collection<T> transform(Collection<F> from);

    T[] toArray(Collection<F> from);
}

and this :

public abstract class BaseDataLayerTransformer<F, T> implements LayerDataTransformer<F, T> {

    @Override
    public List<T> transform(Collection<F> from) {
        List<T> transformed = new ArrayList<>(from.size());

        for (F fromObject : from) {
            transformed.add(transform(fromObject));
        }

        return transformed;
    }

    @Override
    public T[] toArray(Collection<F> from) {
        Class<T> clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
        T[] transformedArray = (T[]) java.lang.reflect.Array.newInstance(clazz, from.size());

        int index = 0;
        for (F fromObject : from) {
            transformedArray[index] = transform(fromObject);
            index++;
        }

        return transformedArray;
    }
}

Usage.

Declare a subclass of BaseDataLayerTransformer

public class FileToStringTransformer extends BaseDataLayerTransformer<File,String> {
    @Override
    public String transform(File file) {
        return file.getAbsolutePath();
    }
}

And use :

FileToStringTransformer transformer = new FileToStringTransformer();
List<File> files = getFilesStub();// returns List<File>
//profit!
String[] filePathArray = transformer.toArray(files);

Solution 11 - Java

I use this simply function. IntelliJ just hates that type cast T[] but it works just fine.

public static <T> T[] fromCollection(Class<T> c, Collection<T> collection) {
    return collection.toArray((T[])java.lang.reflect.Array.newInstance(c, collection.size()));
}

And call looks like this:

Collection<Integer> col = new ArrayList(Arrays.asList(1,2,3,4));    
fromCollection(Integer.class, col);

Solution 12 - Java

This gist that I wrote gives a good solution to this problem.

Following siegi's suggestion on Atreys' answer, I wrote a constructor which finds the "nearest common ancestor" (NCA) class and uses that class to create the array. If checks for nulls and if the provided Collection is length 0 or all nulls, the default type is Object. It totally ignores Interfaces.

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Array;
import java.util.Iterator;

public class FDatum<T> {

  public T[] coordinates;

  // magic number is initial size -- assume <= 5 different classes in coordinates
  public transient HashSet<Class> classes = new HashSet<Class>(5);

  public FDatum (Collection<T> coordinates) {

    // to convert a generic collection to a (sort of) generic array,
    //   we need to bend the rules:

    //   1. default class T is Object
    //   2. loop over elements in Collection, recording each unique class:
    //     a. if Collection has length 0, or
    //        if all elements are null, class T is Object
    //     b. otherwise, find most specific common superclass, which is T

    // record all unique classes in coordinates
    for (T t : coordinates)  this.classes.add(t.getClass());

    // convert to list so we can easily compare elements
    List<Class> classes = new ArrayList<Class>(this.classes);

    // nearest common ancestor class (Object by default)
    Class NCA = Object.class;

    // set NCA to class of first non-null object (if it exists)
    for (int ii = 0; ii < classes.size(); ++ii) {
      Class c = classes.get(ii);
      if (c == null) continue;
      NCA = c; break;
    }

    // if NCA is not Object, find more specific subclass of Object
    if (!NCA.equals(Object.class)) {
      for (int ii = 0; ii < classes.size(); ++ii) {
        Class c = classes.get(ii);
        if (c == null) continue;

        // print types of all elements for debugging
        System.out.println(c);

        // if NCA is not assignable from c,
        //   it means that c is not a subclass of NCA
        // if that is the case, we need to "bump up" NCA
        //   until it *is* a superclass of c

        while (!NCA.isAssignableFrom(c))
          NCA = NCA.getSuperclass();
      }
    }

    // nearest common ancestor class
    System.out.println("NCA: " + NCA);

    // create generic array with class == NCA
    T[] coords = (T[]) Array.newInstance(NCA, coordinates.size());

    // convert coordinates to an array so we can loop over them
    ArrayList<T> coordslist = new ArrayList<T>(coordinates);

    // assign, and we're done!
    for (int ii = 0; ii < coordslist.size(); ++ii)
      coords[ii] = coordslist.get(ii);

    // that's it!
    this.coordinates = coords;
  }

  public FDatum (T[] coordinates) {
    this.coordinates = coordinates;
  }

}

Here are some examples of using it in jshell ("unchecked" class warnings removed for brevity):

jshell> FDatum d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3)))
class java.lang.Double
NCA: class java.lang.Double
d ==> com.nibrt.fractal.FDatum@9660f4e

jshell> d.coordinates
$12 ==> Double[2] { 1.0, 3.3 }

jshell> d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3, (byte)7)))
class java.lang.Byte
class java.lang.Double
NCA: class java.lang.Number
d ==> com.nibrt.fractal.FDatum@6c49835d

jshell> d.coordinates
$14 ==> Number[3] { 1.0, 3.3, 7 }

jshell> d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3, (byte)7, "foo")))
class java.lang.Byte
class java.lang.Double
class java.lang.String
NCA: class java.lang.Object
d ==> com.nibrt.fractal.FDatum@67205a84

jshell> d.coordinates
$16 ==> Object[4] { 1.0, 3.3, 7, "foo" }

Solution 13 - Java

When you have a generic List<T> you will be able to know the class of the object at the runtime. Therefore, the best way to implement it is like this:

public static <T> T[] list2Array(Class<T[]> clazz, List<T> elements)
{
    T[] array = clazz.cast(Array.newInstance(clazz.getComponentType(), elements.size()));
    return elements.toArray(array);
}

Why do you need the Class<T[]> parameter?

Because, we have a generic list and it will not provide the information necessary to get an array of precisely the type we are looking for, of course, while preserving type safety. As opposed to the other answers, which will either give you back an Object array or result in warnings at compile time. This approach will gives you a clean solution. The "hack" here is the clazz.cast() call, which compiles without warnings for whatever type you declare an instance of list2Array().

Now, how can you use it?

Simple, just call it like this:

List<String> list = Stream.of("one", "two", "three").collect(Collectors.toList());
String[] numbers = list2Array(String[].class, list);
System.out.println(Arrays.toString(numbers));

Here is the compiling sample of this: https://ideone.com/wcEPNI

Why does it work?

It works because class literals are treated by the compiler as instances of java.lang.Class. This also works for interfaces, enums, any-dimensional arrays (e.g. String[].class), primitives and the keyword void.

Class itself is generic (declared as Class<T[]>, where T[] stands for the type that the Class object is representing), meaning that the type of String[].class is Class<String[]>.

Note: You won't be able to get an array of primitives, since primitives can't be used for type variables.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFreewindView Question on Stackoverflow
Solution 1 - JavaaioobeView Answer on Stackoverflow
Solution 2 - JavaReverend GonzoView Answer on Stackoverflow
Solution 3 - JavaAtreysView Answer on Stackoverflow
Solution 4 - JavaBuhake SindiView Answer on Stackoverflow
Solution 5 - JavatkruseView Answer on Stackoverflow
Solution 6 - JavamarcolopesView Answer on Stackoverflow
Solution 7 - JavaMartijn CourteauxView Answer on Stackoverflow
Solution 8 - JavaP. CédricView Answer on Stackoverflow
Solution 9 - JavaAlbus DumbledoreView Answer on Stackoverflow
Solution 10 - JavaSergey ShustikovView Answer on Stackoverflow
Solution 11 - JavaAdam OstrožlíkView Answer on Stackoverflow
Solution 12 - JavaawwsmmView Answer on Stackoverflow
Solution 13 - JavaTeocciView Answer on Stackoverflow