BIG
DATA

JAVA

Effective Java Item - 46 : Prefer for-each loops to traditional for loops

Read more about »
  • Java 9 features
  • Read about Hadoop
  • Read about Storm
  • Read about Storm
 

Item 46: Prefer for-each loops to traditional for loops

We have seen in previous article(Item-45), that for loops are better than while loops. But within the for, there are 2 kinds of for loops: traditional for loop and for-each loop. Lets see which is better.

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 = col.iterator(); i.hasNext(); ) {
	doSomething((Element) i.next()); 
}

And 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 < arr.length; i++) {
	doSomething(arr[i]);
}

The iterator and the index variables are both just clutter. Furthermore, they represent opportunities for error. If you do a mistake, 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);
}

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.

The advantages of the for-each loop over the traditional for loop are even greater when it comes to nested iteration over multiple collections. Here is a common mistake that people make when they try to do nested iteration over two collections:

enum Suit { CLUB, DIAMOND, HEART, SPADE }
enum Rank { ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT,
NINE, TEN, JACK, QUEEN, KING }
...
Collection suits = Arrays.asList(Suit.values());
Collection ranks = Arrays.asList(Rank.values());
List deck = new ArrayList();
for (Iterator i = suits.iterator(); i.hasNext(); )
{
	for (Iterator j = ranks.iterator(); j.hasNext(); )
	{
		deck.add(new Card(i.next(), j.next()));
	}
}

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 you find NoSuchElementException for the above code, consider yourself lucky. Because if the size of the outer collection is a multiple of the size of the inner collection, perhaps because if they’re the same collection—the loop will terminate normally, but it won’t do what you want. For example, consider this code to print all of the possible rolls of a pair of dice:

enum Face { ONE, TWO, THREE, FOUR, FIVE, SIX }
...
Collection faces = Arrays.asList(Face.values());
for (Iterator i = faces.iterator(); i.hasNext(); )
{
	for (Iterator j = faces.iterator(); j.hasNext(); )
	{
		System.out.println(i.next() + " " + j.next());
	}
}

This code doesn’t throw an exception but it prints only the six “doubles” (from “ONE ONE” to “SIX SIX”), instead of the expected thirty-six combinations. To fix the bugs in these examples, you must add a variable in the scope of the outer loop to hold the outer element:

// Fixed, but ugly
for (Iterator i = suits.iterator(); i.hasNext(); ) 
{
	Suit suit = i.next();
	for (Iterator j = ranks.iterator(); j.hasNext(); )
	{
		deck.add(new Card(suit, j.next()));
	}
}

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.It is not hard to implement the Iterable interface. If you are writing a type that represents a group of elements, have it implement Iterable even if you choose not to have it implement Collection. This will allow your users to iterate over your type using the for-each loop, and they will be forever grateful.


Situations where you can’t use a for-each loop

  • 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.
  • 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.
  • 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 it- erators or index variables can be advanced in lockstep (as demonstrated unin- tentionally in the buggy card and dice examples above).


Points to remember
  • the for-each loop provides compelling advantages over the traditional for loop in clarity and bug prevention
  • foreach improves readability and maintainability
  • foreach introduces no performance penalty
  • Implement Iterable interface to use custom class in foreach loop
  • foreach is not used in
    • filtering (no access to iterator to call remove)
    • transforming (no access to index element to apply change)
    • parallel iteration (needed few iterators and control locks)