Iterating generic array of any type in Java

10,930

Solution 1

Within your loop, you could use the appropriate array operand for instanceof.

For int[]:

if (e instanceof int[]) {
   // ...
}

For Object arrays (including String[]):

if (e instanceof Object[]){
    // ...
}

Alternatively, when adding your arrays to your master List, you could wrap each one in Arrays.asList(). In that case, you could use the List<List> generic instead of the wildcard generic List<?> and avoid the need to check the data type with instanceof. Something like this:

List<List> list1; 
list1.add(Arrays.asList(new int[2])); 
list1.add(Arrays.asList(new String[3])); 
list1.add(new ArrayList());
for (List e : list1){
    // no need to check instanceof Iterable because we guarantee it's a List
    for (Object object : e) {
        // ...
    }
}

Anytime you're using instanceof and generics together, it's a smell that you may be doing something not quite right with your generics.

Solution 2

Use Array class from reflection package:

    final List<Object> list = new ArrayList<Object>();
    list.add(new int[] { 1, 2 });
    list.add(new String[] { "a", "b", "c" });
    final List<String> arrayList = new ArrayList<String>();
    arrayList.add("el1");
    list.add(arrayList);

    for (Object element : list) {
        if (element instanceof Iterable) {
            for (Object objectInIterable : (Iterable) element) {
                System.out.println(objectInIterable);
            }
        }
        if (element.getClass().isArray()) {
            for (int i = 0; i < Array.getLength(element); i++) {
                System.out.println(Array.get(element, i));
            }
        }
    }

Solution 3

You can't add things to a List<?>. If you want a list of heterogeneous things, use a List<Object>.

However, since you want to iterate over the things in your list, why not use a List<Iterable<Object>>? To add an array, use Arrays.asList(myarray) to get something that implements Iterable from it.

final List<Iterable<? extends Object>> list1 = new ArrayList<Iterable<? extends Object>>();

list1.add(Arrays.asList(new int[2]));
list1.add(Arrays.asList(new String[3])); 
list1.add(new ArrayList<Integer>());

for (final Iterable<? extends Object> e : list1) {
    for (final Object i : e) {
        // ...
    }
}

If you want to store non-iterable things in your list too, then you'll need to use List<Object> and the instanceof check, but you can still use Arrays.asList() to turn arrays into iterables, avoiding the need to handle arrays as a special case.

Solution 4

Arrays do not implement the Iterable interface.

public class StackOverflow
{
    public static void main(final String[] argv)
    {
        display(new int[0].getClass());
    }

    private static void display(final Class clazz)
    {
        final Class   superClass;
        final Class[] interfaces;

        superClass = clazz.getSuperclass();

        if(superClass != null)
        {
            display(superClass);
        }

        System.out.println(clazz.getCanonicalName());
        interfaces = clazz.getInterfaces();

        for(final Class iface : interfaces)
        {
            display(iface);
        }
    }
}

Output:

java.lang.Object
int[]
java.lang.Cloneable
java.io.Serializable

You can use isArray() on the class to see if it is an array:

public class StackOverflow
{
    public static void main(final String[] argv)
    {
        List<Object> list1;

        list1 = new ArrayList<Object>();
        list1.add(new int[2]);
        list1.add(new String[3]);
        list1.add(new ArrayList());

        for(Object e : list1)
        {
            if(e instanceof Iterable)
            {
                System.out.println("Iterable");
            }

            if(e.getClass().isArray())
            {
                System.out.println("array");
            }
        }
    }
}
Share:
10,930
William X
Author by

William X

Updated on June 25, 2022

Comments

  • William X
    William X almost 2 years

    If there is an instance of Java Collection which may carry primitive type, generic array, and/or iterable collection, I want to treat the generic array as Iterable collection, but how? e.g. the following pseudo java code

    List<?> list1; 
    list1.add(new int[2]); 
    list1.add(new String[3]); 
    list1.add(new ArrayList());
    for (Object e : list1){
        if (e instanceof Iterable){ 
            //The int[2] and String[3] will not fall in this case that I want it be
            //Iterate within e
        }
    }
    

    Please advise how to make the int[2] and String[3] fall in the case.

    Thanks & regards, William

    • Petro Semeniuk
      Petro Semeniuk over 13 years
      I could be wrong(I often am), but I think I provided answer where you could iterate over generic collections without knowing exact type.
    • Asaph
      Asaph over 13 years
      @Petro Semeniuk: Your solution works but it makes arrays a special case and treats them differently. The spirit of the original question (at least as I interpreted it) was how can we avoid doing that. How can we make arrays behave just like our other Iterables without treating them separately.
    • Petro Semeniuk
      Petro Semeniuk over 13 years
      @Asaph. Thanks. I really misunderstood original question. Wrapping arrays into collection is really the best solution. Cheers!
  • William X
    William X over 13 years
    is it possible to do something like: "e instanceof T[]", where T is type parameter <T>?
  • William X
    William X over 13 years
    Yup, we can't add element into List<?>. But we can use List<?> in method arguments. I'm just trying to illustrate such case, sorry for making confuse. Return to the question, i'm really curious about if there exists a way to do something like "e instanceof T[]" where "T" is type parameter.
  • Asaph
    Asaph over 13 years
    @William Choi: No. That is not possible because of type erasure. instanceof happens at runtime and T[] is lost after compile time.
  • William X
    William X over 13 years
    got it. I've misunderstood that the T[] will be kept during run-time.
  • Wyzard
    Wyzard over 13 years
    If all you need to do with the items is iterate over them, use a List<Iterable>, not a List<List>. That way you have the flexibility to store other iterable things like Set as well.
  • Wyzard
    Wyzard over 13 years
    @William Choi, no, you can't do that, because the type parameter T only exists at compile time; at runtime it's effectively just Object. If you need to know the parameterized type at runtime, you can make the caller pass in a parameter of type Class<T>. The compiler will enforce that only the correct class object can be passed, and you can use methods like isInstance(), isArray(), and getComponentType() on the class object.