Friday, September 07, 2007

The Enhanced For Loop

This Tech Tip reprinted with permission by java.sun.com

Introduced as a new language feature in J2SE 5.0, the enhanced for loop allows you to iterate through a collection without having to create an Iterator or without having to calculate beginning and end conditions for a counter variable. The enhanced for loop is the easiest of the new features to immediately incorporate in your code. In this tip you will see how the enhanced for loop replaces more traditional ways of sequentially accessing elements in a collection.

So what does an enhanced for loop look like? Suppose you have a collection of TechTip objects called RecentTips. You could use an enhanced for loop with the collection as follows:

for (TechTip tip: RecentTips)

You read this as "for each TechTip in RecentTips". Here the variable tip is used to point to the current instance of TechTip in the collection. Because of the "for each" wording, the enhanced-for construct is also referred to as the for-each construct.

If you compare the enhanced for loop to the typical way of iterating over a collection, it's clear that the for each loop is simpler and can make your code more readable.

Also note that the enhanced for loop is designed to simplify your work with generics. Although this tip does include two examples of using the enhanced for loop with generics, it's not the focus of the tip. Instead the objective of this tip is to introduces you to the more basic changes you can make to your code to use the for each loop.

First, consider how you might use a for loop to iterate through the elements of an Array. For simplicity, load an array with six ints that represent the squares of the ints from zero to five. Here's a for loop that does the iteration:

for (int i=0; i< squares.length; i++)

The line illustrates the classic use of the for loop. It specifies the initial value of one or more counters, sets up a terminating condition, and describes how the counters might be incremented.

Here is a short program, OldForArray, that uses the for loop.
public class OldForArray {

public static void main(String[] args){
int[] squares = {0,1,4,9,16,25};
for (int i=0; i< squares.length; i++){
System.out.printf("%d squared is %d.\n",i, squares[i]);
}
}
}

Compile and run the OldForArray program, and you get the following:

0 squared is 0.
1 squared is 1.
2 squared is 4.
3 squared is 9.
4 squared is 16.
5 squared is 25.

If you change the test program to use the enhanced for loop, you specify the relevant variable and the collection that the variable comes from. Here is a line that's an enhanced for loop:

for (int i : squares)

You can read the line as "iterate on elements from the collection named squares. "The current element will be referenced by the int i."

You don't need to determine how many elements are in the array before looping. There is also no need to specify how to increment the current position. "Under the covers," the enhanced for loop for an array is equivalent to the for loop presented earlier.

The test program NewForArray gives the same result as OldForArray.
public class NewForArray {

public static void main(String[] args) {
int j = 0;
int[] squares = {0, 1, 4, 9, 16, 25};
for (int i : squares) {
System.out.printf("%d squared is %d.\n", j++, i);
}
}
}

An array is an indexed collection of elements of a single type that is specified when the array is declared. With more general collections such as ArrayLists, the elements are stored as Objects. You could use the C style for loop to iterate through a collection like this:

for (int i = 0; i < list.size(); i++)

You then can use list.get(i) to reference the current element. For example, the following program, OldForArrayList, uses autoboxing to fill the ArrayList and to then read from the ArrayList:
import java.util.ArrayList;
import java.util.List;

public class OldForArrayList {
private static List squares = new ArrayList();

private static void fillList() {
for (int i = 0; i < 6; i++) {
squares.add(i * i);
}
}

private static void outputList() {
for (int i = 0; i < squares.size(); i++) {
System.out.printf("%d squared is %d.\n",
i, squares.get(i));
}
}

public static void main(String args[]) {
fillList();
outputList();
}
}

However, because ArrayList is part of the collections framework, it is more common to iterate through it using an Iterator in the following pattern:
while( iterator.hasNext()) {
doSomethingWith (iterator.next());
}

You can bundle this in a for loop as shown in the following program, IteratorForArrayList:
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;

public class IteratorForArrayList {

private static List squares = new ArrayList();

private static void fillList() {
for (int i = 0; i < 6; i++) {
squares.add(i * i);
}
}

private static void outputList() {
Iterator iterator = squares.iterator();
int j=0;
for (; iterator.hasNext();) {
System.out.printf("%d squared is %d.\n",
j++, iterator.next());
}
}

public static void main(String args[]) {
fillList();
outputList();
}
}

It looks a bit unusual to have a for loop with no first or third parameter. There is no initial condition, and the incrementing of the position in the List is performed in the body of the for loop with the call iterator.next().

The enhanced for loop makes the explicit use of an iterator unnecessary. Rather than create an Iterator object for the ArrayList and then use the iterator in the for loop, you use the following:

for ( Integer square : squares)

This indicates that the name of the collection is squares. It also indicates that the currently referenced item is of type Integer and is referenced by the variable square.

This code will not compile because there is no way of knowing that the contents of the ArrayList is of type Integer. To fix this, you need to use another feature introduced in J2SE 5.0, namely generics. You need to specify in the declaration and definition of squares that it can only hold elements of type Integer. You do this as follows:

private static List squares
= new ArrayList();

The following program, NewArrayList, shows how to use the enhanced for loop together with generics:
import java.util.List;
import java.util.ArrayList;

public class NewArrayList {
private static List squares
= new ArrayList();

private static void fillList() {
for (int i = 0; i < 6; i++) {
squares.add(i * i);
}
}

private static void outputList() {
int j=0;
for (Integer square : squares) {
System.out.printf("%d squared is %d.\n",
j++, square);
}
}

public static void main(String args[]) {
fillList();
outputList();
}
}

This NewArrayList example is a bit simplistic, but it demonstrates the syntactic differences between using the classic for loop and the enhanced for loop. Here is another example that compares the syntactic differences between the loops. The example is excerpted from a talk given by Joshua Bloch and Neil Gafter at the 2004 JavaOne Conference. In the example, a method is applied to each element in a collection. To start, the example uses an Iterator like this:
void cancelAll (Collection c) {
for (Iterator i = c.iterator(); i.hasNext(); ) {
TimerTask tt = (TimerTask) i.next();
tt.cancel();
}
}

Next, an enhanced for loop is introduced to eliminate the use of the Iterator:
void cancelAll( Collection c ) {
for (Object o : c)
( (TimerTask) o). cancel();
}

There is still a matter of having to treat the elements of the collection as being of type Object and then casting them to type TimerTask. This is fixed by introducing generics like this:
void cancelAll( Collection c ) {
for (TimerTask task : c)
task.cancel();
}

It's important to note that the enhanced for loop can't be used everywhere. You can't use the enhanced for loop:

* To remove elements as you traverse collections
* To modify the current slot in an array or list
* To iterate over multiple collections or arrays

However aside from these cases, you should try to use the enhanced for loop code to simplify your code.

As with anything new, the syntax for the enhanced for loop might seem unfamiliar and difficult to read. You have probably used the C style for loop for many years and possibly in more than one language. It is, however, cleaner not having to create a counter variable or an Iterator. You also do not need to worry about where your collection begins and ends to set up the initial value and loop termination conditions.

For more information on the enhanced for loop, see The For-Each Loop.

Copyright (c) 2004-2005 Sun Microsystems, Inc.
All Rights Reserved.

No comments: