Can I write a for loop that iterates over both collections and arrays?

Regarding a condition to check if foo is either a collection or an array:

Class#isAssignableFrom may come in handy.

Class<?> fooClass = foo.getClass();
boolean isArrayOrCollection = Collection.class.isAssignableFrom(fooClass) ||
                              Object[].class.isAssignableFrom(fooClass);

I reasonably assume you won't test it on primitive arrays since you have collections which work only with the wrapper classes.

I guess you can safely replace Object[].class.isAssignableFrom(fooClass) with fooClass.isArray()

boolean isArrayOrCollection = Collection.class.isAssignableFrom(fooClass) ||
                              fooClass.isArray();

and it would also work for a primitive array class.


I've run a small "test"

class Test {
    public static void main(String[] args) {
        Predicate<Class<?>> p = c -> Collection.class.isAssignableFrom(c) || 
                                     c.isArray();

        System.out.println(p.test(new int[0].getClass()));
        System.out.println(p.test(new Integer[0].getClass()));
        System.out.println(p.test(Collections.emptyList().getClass()));
        System.out.println(p.test(Collections.emptySet().getClass()));

        System.out.println(p.test(Collections.emptyMap().getClass()));
    }
}

which results in

true
true
true
true
false

Regarding a generic loop that would run over both arrays and collections:

You simply can't write an accurate construction to handle this: Collection (or Iterable) and Object[] have little in common (Object as a common parent and its methods are not enough).

I think it's sensible to build own abstraction which would treat collections and arrays in the same manner. Having no particular context, I can come up with a simple idea of two subclasses, each of which defining how its source (either a collection or an array) should be iterated. Then, programming to an interface will help to manage them equally.

A very simplified example would be:

interface Abstraction<T> {
    void iterate(Consumer<? super T> action);

    static <T> Abstraction<T> of(Collection<T> collection) {
        return new CollectionAbstraction<>(collection);
    }
    static <T> Abstraction<T> of(T[] array) {
        return new ArrayAbstraction<>(array);
    }
    static IntArrayAbstraction of(int[] array) {
        return new IntArrayAbstraction(array);
    }
}

class CollectionAbstraction<T> implements Abstraction<T> {
    Collection<T> source;

    public CollectionAbstraction(Collection<T> source) {
        this.source = source;
    }

    @Override
    public void iterate(Consumer<? super T> action) {
        source.forEach(action);
    }
}

class ArrayAbstraction<T> implements Abstraction<T> {
    T[] source;

    public ArrayAbstraction(T[] source) {
        this.source = source;
    }

    @Override
    public void iterate(Consumer<? super T> action) {
        for (T t : source) {
            action.accept(t);
        }
    }
}

class IntArrayAbstraction implements Abstraction<Integer> {
    int[] source;

    public IntArrayAbstraction(int[] source) {
        this.source = source;
    }

    @Override
    public void iterate(Consumer<? super Integer> action) {
        for (int t : source) {
            action.accept(t);
        }
    }
}

class Test {
    public static void main(String[] args) {
        Abstraction.of(new Integer[] {1, 2, 3}).iterate(System.out::println);
        Abstraction.of(Arrays.asList(1, 2, 3)).iterate(System.out::println);
        Abstraction.of(new int[] {1, 2, 3}).iterate(System.out::println);
    }
}

I believe the approach above is pretty versatile. You don't depend on how a certain source is iterated, you may selectively modify them.


The other answers are all trying hard to answer the original title question:

Is there a common interface or superclass for arrays and collections?

But your real question is in the body:

Are there any other possibilities to handle both in one loop like the above?

The answer is: No, there's no way to write a single for loop that iterates over both collections and arrays.

You could jump through a bunch of hoops to turn the arrays into lists, but you'll almost certainly end up with a bigger mess than if you just wrote two (or more) loops. Calling getClass().isArray() tells you what you have but you still can't work with it without some sort of cast. Arrays.asList() doesn't work for arrays of primitives.


Depending on what you are trying to do, you might want to implement two similar methods:

public <T> void iterateOver(List<T> list) {
    // do whatever you want to do with your list
}

public <T> void iterateOver(T[] array) {
    this.iterateOver(Arrays.asList(array));
}

Or maybe even have an interface for this:

interface ExtendedIterableConsumer<T> {

    public void iterateOver(List<T> list);

    public default void iterateOver(T[] array) {
        this.iterateOver(Arrays.asList(array));

}

I am not sure if that helps you, because you seem to already have the object in question in a variable somewhere. But if you can address that problem one level higher, it might be useful.