Iterating Arrays and Collections

When you traverse arrays and collections, you typically write code like this:

// some list
List<String> list = new ArrayList<>();

// iterate a collection - for loop
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String element = iterator.next();
    // do something
}

// iterate a collection - while loop
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    // do something
}

// iterate an array
String[] arr = new String[]{"a", "b", "c"};
for (int index = 0; index < arr.length; index++) {
    String element = arr[index];
    // do something
}

These loops add iterator and index variables that clutter the code. They are not the real goal, and mistakes with them can cause bugs.


for-each

Most of the time, you only need the element itself, so use a for-each loop. The for-each loop is the enhanced for statement. It avoids iterators and index variables, so the code is cleaner and less error-prone.

// traditional for loop
for (int index = 0; index < arr.length; index++) {
    String element = arr[index];
    // do something
}

// for each
for (String element : arr) {
    // do something
}

You can read the colon as “in”: “each element in arr.” The advantage grows with nested loops.

// nested for-each
for (Suit suit : suits) {
    for (Rank rank : ranks) {
        deck.add(new Card(suit, rank));
    }
}


When You Cannot Use for-each

There are cases where for-each is not an option.

Filtering: If you need to remove selected elements while iterating, you must use an explicit Iterator so you can call remove.

If you modify the list directly inside a for-each, you get ConcurrentModificationException. Lists do not allow structural changes during iteration.

// some list
List<String> list = new ArrayList<>();
list.add("a"); list.add("b"); list.add("c");

for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String element = iterator.next();
    if(element.equals("a")) {
        iterator.remove();
    }
    // do something
}

With Java 8, you can use removeIf and avoid explicit iteration.

// Lambda
list.removeIf(s -> s.equals("a"));

// expanded version
list.removeIf(new Predicate<String>() {
    @Override
    public boolean test(String s) {
        return s.equals("a");
    }
});

Transforming: If you need to modify elements in place, you must use an iterator or array index.

// some array
String[] arr = new String[]{"a", "b", "c"};

// must use index
for (int index = 0; index < arr.length; index++) {
    String s = arr[index];
    if(s.equals("a")) {
        arr[index] = "d";
    }
}

Parallel iteration: If you need to iterate multiple collections in lockstep, you must control the iterators or indices explicitly. Otherwise, you get errors like this.

enum Suit {
    CLUB, DIAMOND, HEART, SPADE
}

enum Rank {
    ACE, DEUCE, THREE, FOUR, 
    // ... omitted 
    QUEEN, KING
}

List<Card> deck = new ArrayList<>();
List<Suit> suits = Arrays.asList(Suit.values());
List<Rank> ranks = Arrays.asList(Rank.values());
for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ) {
    for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) {
        // next should be called once per suit,
        // but it is called once per rank
        deck.add(new Card(i.next(), j.next()));
    }
} 

next should run once per Suit, but it runs once per Rank, so eventually you get a NoSuchElementException.

In these cases, use the traditional for loop. for-each works with arrays, collections, and anything that implements Iterable.