Prior to
release 1.5, this was the preferred idiom for iterating over a collection:
// No longer the preferred idiom to iterate over a
collection!
for (Iterator i = c.iterator(); i.hasNext(); ) {
doSomething((Element) i.next()); // (No generics before
1.5)
}
This was the
preferred idiom for iterating over an array:
// No longer the preferred idiom to iterate over an
array!
for (int i = 0; i < a.length; i++) {
doSomething(a[i]);
}
These idioms
are better than while loops (Item 45),
but they aren’t perfect. The iterator and the index variables are both just
clutter. Furthermore, they represent opportunities for error. The iterator and
the index variable occur three times in each loop, which gives you two chances
to get them wrong. If you do, there is no guarantee that the compiler will
catch the problem.
The for-each
loop, introduced in release 1.5, gets rid of the clutter and the opportunity
for error by hiding the iterator or index variable completely. The resulting
idiom applies equally to collections and arrays:
// The preferred idiom for iterating over
collections and arrays
for (Element e : elements) {
doSomething(e);
}
When you see
the colon (:), read it as “in.” Thus, the
loop above reads as “for each element e in elements.” Note that there is no
performance penalty for using the for-each loop, even for arrays. In fact, it
may offer a slight performance advantage over an ordinary for loop in some circumstances, as it computes
the limit of the array index only once. While you can do this by
hand (Item 45), programmers don’t always do so.
The advantages
of the for-each loop over the traditional for
loop
are even greater when it comes to nested iteration over multiple collections.
// Can you spot the bug?
enum Suit { CLUB, DIAMOND, HEART, SPADE }
enum Rank { ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN,
EIGHT,
NINE, TEN, JACK, QUEEN, KING }
...
Collection<Suit> suits =
Arrays.asList(Suit.values());
Collection<Rank> ranks =
Arrays.asList(Rank.values());
List<Card> deck = new ArrayList<Card>();
for (Iterator<Suit> i = suits.iterator();
i.hasNext(); )
for (Iterator<Rank> j = ranks.iterator();
j.hasNext(); )
deck.add(new Card(i.next(), j.next()));
Don’t feel bad
if you didn’t spot the bug. Many expert programmers have made this mistake at
one time or another. The problem is that the next
method
is called too many times on the iterator for the outer collection (suits). It should be called from the outer loop,
so that it is called once per suit, but instead it is called from the inner
loop, so it is called once per card. After you run out of suits, the loop
throws a NoSuchElementException.
If instead you
use a nested for-each loop, the problem simply disappears. The resulting code
is as succinct as you could wish for:
// Preferred idiom for nested iteration on
collections and arrays
for (Suit suit : suits)
for (Rank rank : ranks)
deck.add(new Card(suit, rank));
Not only does
the for-each loop let you iterate over collections and arrays, it lets you
iterate over any object that implements the Iterable
interface.
In summary,
the for-each loop provides compelling advantages over the traditional for loop in clarity and bug prevention, with no
performance penalty. You should use it wherever you can. Unfortunately, there
are three common situations where you can’t use a for-each loop:
1. Filtering—If you need to
traverse a collection and remove selected elements, then you need to use an
explicit iterator so that you can call its remove
method.
2. Transforming—If you need to traverse a list or array and replace some or all of the values of its elements, then you need the list iterator or array index in order to set the value of an element.
3. Parallel
iteration—If you need to traverse multiple collections in parallel, then you
need explicit control over the iterator or index variable, so that all
iterators or index variables can be advanced in lockstep.
If you find
yourself in any of these situations, use an ordinary for loop, be wary of the traps mentioned in this
item, and know that you’re doing the best you can.
Reference: Effective Java 2nd Edition by Joshua Bloch