Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

Is there a possibility to check if an object is either an array or a collection with one clause? What I am trying to achieve:

Assuming arrays implement Iterable, and assuming the Object foo could be either an array or a collection, I'd like to use a code snippet like this:

if (foo instanceof Iterable) {
  for (Object f : (Iterable) foo) {
    // do something with f
  }
}

Unfortunately, an array cannot be cast to Iterable. Nor does it implement Collection. Are there any other possibilities to handle both in one loop like the above? Instead of -- of course -- using an if-else if-clause and two loops (which wouldn't be nice).

Edit: In response to these answers. I am aware of the isArray() method but in this case the casting in

...
for (Object f : (Iterable) foo) {
...

will fail. That'd a pity and a code redundancy since I would have to use two loops although a foreach-loop works both with Collections and Arrays.

like image 668
Pawel Os. Avatar asked Apr 09 '19 08:04

Pawel Os.


People also ask

Can you use a For Each loop on an array?

The forEach method is also used to loop through arrays, but it uses a function differently than the classic "for loop". The forEach method passes a callback function for each element of an array together with the following parameters: Current Value (required) - The value of the current array element.

How do you write a forEach loop that will iterate over array?

For-Each Loop is another form of for loop used to traverse the array. for-each loop reduces the code significantly and there is no use of the index or rather the counter in the loop. Syntax: For(<DataType of array/List><Temp variable name> : <Array/List to be iterated>){ System.

What can a for each loop iterate over?

In Java, the for-each loop is used to iterate through elements of arrays and collections (like ArrayList). It is also known as the enhanced for loop.

What type of loop is the most appropriate for iterating a 2D array?

Looping Through a 2D Array. Since you can find out the number of rows and columns in a 2D array you can use a nested for loop (one loop inside of another loop) to loop/traverse through all of the elements of a 2D array.


2 Answers

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.

like image 162
Andrew Tobilko Avatar answered Oct 12 '22 22:10

Andrew Tobilko


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.

like image 31
John Kugelman Avatar answered Oct 12 '22 21:10

John Kugelman