Iterating generic array of any type in Java
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");
}
}
}
}
William X
Updated on June 25, 2022Comments
-
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 over 13 yearsI could be wrong(I often am), but I think I provided answer where you could iterate over generic collections without knowing exact type.
-
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 over 13 years@Asaph. Thanks. I really misunderstood original question. Wrapping arrays into collection is really the best solution. Cheers!
-
-
William X over 13 yearsis it possible to do something like: "e instanceof T[]", where T is type parameter <T>?
-
William X over 13 yearsYup, 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 over 13 years@William Choi: No. That is not possible because of type erasure.
instanceof
happens at runtime andT[]
is lost after compile time. -
William X over 13 yearsgot it. I've misunderstood that the T[] will be kept during run-time.
-
Wyzard over 13 yearsIf all you need to do with the items is iterate over them, use a
List<Iterable>
, not aList<List>
. That way you have the flexibility to store other iterable things likeSet
as well. -
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 likeisInstance()
,isArray()
, andgetComponentType()
on the class object.