5.3 The Enhanced For-Each Loop
Key Takeaways
- The enhanced for loop (for-each) provides a concise, index-free syntax for forward-only sequential traversal of arrays and java.lang.Iterable collections.
- When traversing primitive arrays, the loop control variable receives a copy of each element's value, making the traversal strictly read-only for array contents.
- When traversing object collections, the loop variable holds a copy of the reference pointer; mutating the referenced object modifies heap state, but reassigning the variable does not affect array or collection slots.
- Structurally modifying a collection (calling list.add() or list.remove()) during an enhanced for-loop traversal is a defect that normally throws ConcurrentModificationException on the next iteration step; removing the second-to-last element is the one data-dependent case that escapes detection because hasNext() ends the loop first.
- Enhanced for loops cannot access current index positions, cannot traverse backwards, cannot skip elements, and cannot traverse multiple data structures in parallel.
5.3 The Enhanced For-Each Loop
[!NOTE] Exam Focus: Introduced in Java 5, the enhanced
forloop (commonly termed the "for-each" loop) carries its own objective inside Oracle's "Using Looping Statements" topic area: "use a for loop including an enhanced for loop". The 1Z0-811 examination specifically tests candidates on its syntax rules, its underlying read-only mechanics regarding primitive elements, the runtimeConcurrentModificationExceptioncaused by altering collections during iteration, and the scenarios where developers must revert to standard indexed loops.
Traditional indexed for loops require declaring a counter variable, establishing a boundary expression (such as i < array.length), and manually updating the counter (i++). This repetitive boilerplate code introduces frequent opportunities for off-by-one errors and indexing bugs. To eliminate these hazards when simply inspecting or aggregating sequential data, Java provides the enhanced for loop.
Syntax and Semantic Structure
The enhanced for loop eliminates explicit counter variables and index subscripts entirely, replacing them with a variable declaration and a target expression separated by a colon (:):
for (elementType variableName : targetExpression) {
// Loop body: executes once for each element in targetExpression
}
Syntactic Components
- Element Declaration (
elementType variableName): Declares a block-scoped local variable. Its data type must be assignable from (compatible with) the elements of the array or collection being traversed. - Target Expression (
targetExpression): Must evaluate to either an array or an instance of a class that implements thejava.lang.Iterableinterface (such asArrayList,HashSet, orLinkedList). Passing a primitive type,null, or an arbitrary non-iterable class triggers an immediate compile-time error (foreach not applicable to type). - Colon (
:): Read aloud as "in". For example,for (String name : names)is read as "for each Stringnameinnames".
String[] colors = {"Red", "Green", "Blue"};
for (String color : colors) {
System.out.println(color);
}
Traversal Across Arrays and Collections
1. One-Dimensional Arrays
In a single-dimensional array, the enhanced for loop extracts elements sequentially from index 0 up to array.length - 1:
int[] grades = {85, 92, 78, 96};
int sum = 0;
for (int grade : grades) {
sum += grade;
}
System.out.println("Average: " + (sum / (double) grades.length)); // Prints: Average: 87.75
2. Multi-Dimensional and Ragged (Jagged) Arrays
In Java, a two-dimensional array is an "array of arrays". Consequently, traversing a 2D array with enhanced for loops requires nested loops:
- The outer loop retrieves each one-dimensional array row (
int[] row). - The inner loop extracts each individual primitive integer (
int val) from that row.
int[][] matrix = {
{1, 2},
{3, 4, 5} // Ragged row with 3 elements
};
for (int[] row : matrix) { // Outer loop extracts 1D array row
for (int val : row) { // Inner loop extracts primitive values
System.out.print(val + " ");
}
}
// Output: 1 2 3 4 5
Notice that ragged arrays with uneven row lengths are processed seamlessly because the inner loop automatically binds to the specific length of each row.
3. ArrayList Collections and Autoboxing
When traversing an ArrayList, the declared variable type must be compatible with the list's generic element type. Java automatically handles autoboxing and unboxing:
ArrayList<Integer> scores = new ArrayList<Integer>();
scores.add(90); // Autoboxed from int to Integer
scores.add(85);
// Unboxes Integer object to primitive int automatically
for (int score : scores) {
System.out.println("Score: " + score);
}
Compiler Desugaring Mechanics
The enhanced for loop is "syntactic sugar"—a high-level language convenience that the Java compiler (javac) translates into standard low-level bytecode patterns:
1. Desugaring for Arrays
When compiling an enhanced for loop over an array, javac generates an internal temporary index variable and a standard indexed for loop:
// What you write:
for (int num : numbers) {
System.out.println(num);
}
// What javac generates internally:
int[] tempArray = numbers;
for (int tempIdx = 0; tempIdx < tempArray.length; tempIdx++) {
int num = tempArray[tempIdx];
System.out.println(num);
}
2. Desugaring for Iterable Collections
When compiling an enhanced for loop over an ArrayList or other Iterable, javac generates an explicit java.util.Iterator loop:
// What you write:
for (String item : itemList) {
System.out.println(item);
}
// What javac generates internally:
for (Iterator<String> it = itemList.iterator(); it.hasNext(); ) {
String item = it.next();
System.out.println(item);
}
The Read-Only Traversal Nature: Primitive Arrays
One of the most heavily tested traps on the 1Z0-811 examination concerns attempting to modify array data within an enhanced for loop.
The Local Copy Trap
When an enhanced for loop traverses an array of primitives, the loop control variable is an independent local stack variable that receives a copy of the value stored in the array at that index. Reassigning the loop variable has zero effect on the underlying array:
int[] data = {10, 20, 30};
for (int x : data) {
x = x * 2; // Modifies only the local stack variable x!
}
System.out.println(data[0] + ", " + data[1] + ", " + data[2]);
// Output: 10, 20, 30 (UNMODIFIED!)
To modify array elements in place, a traditional indexed for loop must be used (data[i] = data[i] * 2;).
Object Traversal: Reference Copying vs. State Mutation
When traversing an array or collection of objects, understanding the difference between modifying an object's internal state versus reassigning the reference variable is crucial:
class Account {
int balance;
Account(int b) { balance = b; }
}
Account[] accounts = { new Account(100), new Account(200) };
for (Account acc : accounts) {
acc.balance += 50; // 1. MUTATES HEAP OBJECT: Changes underlying object state!
acc = new Account(999); // 2. REASSIGNS LOCAL REFERENCE: Has zero effect on array!
}
System.out.println(accounts[0].balance); // Prints 150, NOT 999 or 100!
System.out.println(accounts[1].balance); // Prints 250, NOT 999 or 200!
- Mutating Heap State (
acc.balance += 50): The variableaccholds a copy of the reference pointer pointing to the heap object. Invoking mutators or updating fields directly modifies the actual object in memory. This mutation is reflected in the array. - Reassigning the Reference (
acc = new Account(999)): Reassigningaccmerely points the local stack variable to a newly instantiated object on the heap. The original reference stored in theaccountsarray remains completely unchanged.
Critical Limitations of the Enhanced for Loop
Despite its clarity and conciseness, the enhanced for loop is unsuitable in several common programming scenarios:
1. Structural Modification of Collections (ConcurrentModificationException)
You cannot add or remove elements from an ArrayList or collection while traversing it with an enhanced for loop. Because the loop compiles to an internal Iterator, modifying the list directly causes the iterator's modification count (modCount) to fall out of sync with its expected count, triggering a runtime exception:
ArrayList<String> names = new ArrayList<String>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
for (String name : names) {
if (name.equals("Alice")) {
names.remove(name); // RUNTIME ERROR: java.util.ConcurrentModificationException
}
}
To safely remove elements during traversal, developers must use an explicit Iterator and invoke its remove() method (iterator.remove()).
[!CAUTION] The Second-to-Last Element Loophole (Know This Precisely): The
ConcurrentModificationExceptionis not raised byremove()itself — it is raised by the next call toit.next(), which compares the list'smodCountagainst the iterator's expected count. The iterator'shasNext()simply testscursor != size. So if you remove the second-to-last element,sizedrops to exactly equalcursor,hasNext()returnsfalse, the loop exits normally, and no exception is ever thrown:ArrayList<String> ids = new ArrayList<String>(); ids.add("A101"); ids.add("B202"); ids.add("C303"); for (String id : ids) { if (id.equals("B202")) { // B202 is the second-to-last element ids.remove(id); // No exception! Loop simply ends early. } } System.out.println(ids); // Prints: [A101, C303]This is exactly why structural modification during a for-each loop is a defect even when it appears to "work": the failure is data-dependent. Removing
"A101"from the same list throws immediately, while removing"B202"silently succeeds.
2. Lack of Index Access
The enhanced for loop does not expose the current index position. If your algorithm requires the index—such as printing line numbers, updating specific array slots, or searching for an index—maintaining an external counter defeats the architectural purpose of the construct.
3. Forward-Only, Single-Step Traversal
Enhanced for loops move strictly forward, visiting every element from first to last in single steps. They cannot:
- Traverse in reverse order (e.g., from
length - 1down to0). - Skip elements (e.g., visiting only even indices with
i += 2).
4. Parallel Data Structure Traversal
You cannot iterate through two arrays or collections simultaneously in lockstep (e.g., matching students[i] with grades[i]).
Architectural Comparison: Standard vs. Enhanced for Loops
| Capability / Requirement | Standard for Loop | Enhanced for Loop |
|---|---|---|
| Access Current Index | Yes (i directly accessible) | No (requires external counter variable) |
| Modify Array Primitives | Yes (array[i] = newVal;) | No (reassigning loop variable does nothing) |
| Reverse / Custom Stride | Yes (i--, i += 2) | No (strictly forward by 1 element) |
| Remove During Traversal | Yes (with careful index tracking) | No (throws ConcurrentModificationException) |
| Safety from Bounds Bugs | Error-prone (< vs <=, off-by-one) | Immune to off-by-one indexing errors |
| Primary Use Cases | Element mutation, indexed access, stepping | Read-only iteration, summing, searching |
What is the output of the following Java program?
class Item {
int qty;
Item(int q) { this.qty = q; }
}
Item[] items = { new Item(5), new Item(8) };
for (Item it : items) {
it.qty += 10;
it = new Item(99);
}
System.out.println(items[0].qty + "," + items[1].qty);
Consider the following code snippet that manipulates a list of customer identifiers:
What is the outcome when this code is executed?ArrayList<String> ids = new ArrayList<String>();
ids.add("A101");
ids.add("B202");
ids.add("C303");
for (String id : ids) {
if (id.equals("A101")) {
ids.remove(id);
}
}
What is the output of the following Java program?
int[][] matrix = {
{1, 2},
{3},
{4, 5, 6}
};
int count = 0;
int sum = 0;
for (int[] row : matrix) {
for (int val : row) {
count++;
sum += val;
}
}
System.out.println("count=" + count + ", sum=" + sum);