Skip to main content

Section 4.6 For-Each Loops: Syntactic Sugar for Iteration

In the previous section, we built a custom iteration class (MyIterator) that encapsulated an array and a current index—essentially acting as a "bookmark" into the data. That approach frees us from manually writing for loops (with index checks) everywhere.
However, imagine if you could simply hand off your array to a built-in mechanism that knows how to iterate without exposing the raw index. In Java, the enhanced for loop (often called the for-each loop) provides exactly this convenience. It’s a form of syntactic sugar—a concise way to perform iteration without writing out all the loop-control details.
Our goal in this section is to show you how to use the for-each loop with arrays, compare its behavior to the iteration logic you already built, and discuss subtle pitfalls to watch out for. Later, as you learn about interfaces and inheritance, you’ll see how to adapt your custom classes so that they plug directly into the for-each syntax.
Expectations: By the end of this section, you will be able to:
  • Use the enhanced for loop to iterate over arrays succinctly
  • Recognize that for-each loops hide index details (like hasNext(), next() calls)
  • Explain common pitfalls and limitations of for-each loops
  • Relate this "syntactic sugar" back to our earlier design of MyIterator

Subsection 4.6.1 Using For-Each Loops with Arrays

Consider a simple array of integers. Traditionally, you might iterate like this:
int[] numbers = {5, 10, 15, 20};
for (int i = 0; i < numbers.length; i++) {
    System.out.println("Index " + i + ": " + numbers[i]);
}
In a for-each loop, we can omit the manual index management:
int[] numbers = {5, 10, 15, 20};
for (int num : numbers) {
    System.out.println("Number: " + num);
}
The syntax for (int num : numbers) is short for “Loop over each element of numbers, assign it to num, then run the body.” It’s the same fundamental iteration—just no explicit index variable or length checks. Think of it like Java calling hasNext() and next() behind the scenes for each element.
Visual Concept:
Numbers array:   [  5  |  10  |  15  |  20  ]
Indices:           0      1      2      3
For-each loop =>  first pass: num=5
                  second pass: num=10
                  third pass: num=15
                  fourth pass: num=20
Each iteration, num becomes one element of numbers. Once it exhausts the array, the loop ends automatically—no extra condition or index update required.

Subsection 4.6.2 Common Pitfalls & Limitations

The for-each loop is convenient, but let’s highlight where it can trip you up:
  • No Direct Index Accesswhichi+1for
    Example: for(int x : arr) doesn’t let you do arr[i+1].
  • No Easy Way to Remove/Insert: Because arrays are fixed-size and for-each doesn’t provide the index, you can’t remove or insert elements mid-loop (like you might in a dynamic list). You can modify the element values themselves, but you can’t skip or reorder them easily using for-each.
  • Accidental Shadowing or Reassignmentnumfor (int num : numbers)numnumbers
    Example: for (int x : arr) { x = 99; } doesn’t actually set arr[anything] to 99; you’re merely changing the local variable x.
  • Breaking Early or Skipping Elements: If you need to exit after you find a certain element, you can do so with a break inside the for-each body—but skipping certain indices or iterating in reverse is better handled by a classic for loop or a while.
Overall, for-each loops excel at “visit every element in a forward direction with minimal fuss.” If you want more control—like indexing, reordering, or partial iteration—then a classic for or while might be better.

Subsection 4.6.3 Relating For-Each to Our MyIterator Class

Recall that our MyIterator class had methods hasNext() and next() to manage iteration. Java’s for-each loop relies on the same idea but in a more standardized way using interfaces and generics:

Note 4.6.1.

An interface in Java defines a contract that a class must follow if it implements that interface. The Iterator<T> and Iterable<T> interfaces formalize how iteration should work, ensuring consistency across different types.
Generics (<T>) allow these interfaces to work with any data type rather than being restricted to a specific one. This makes iterators more reusable and type-safe.
But don’t worry too much about interfaces and generics for now, we’ll cover them in more detail later. This is just a stepping stone to help us see how Java handles iteration under the hood. The key idea is that Java’s for-each loop works by relying on hasNext() and next(), just like our MyIterator class.
  • If a class implementsIterable<T>, it must provide public Iterator<T> iterator().
  • That Iterator<T> must have two key methods:
    • boolean hasNext() - Checks if more elements are available.
    • T next(): Returns the next element in the sequence.
  • Then for (T item : iterableObject) automatically calls hasNext() and next() behind the scenes for each item.
Our MyIterator is close to that pattern. Later, once we learn about interfaces and generics, we can rework MyIterator to formally implement Iterator<Integer>. We could also create a separate class that implements Iterable<Integer> (returning a new MyIterator in its iterator() method). Then for-each would just “magically” work.
Thus, the for-each loop is essentially “syntactic sugar” for iterators, as you can see in the pseudo-code below:
// Pseudo-code of what for (T item : collection) is doing:
Iterator<T> it = collection.iterator();
while (it.hasNext()) {
    T item = it.next();
    // loop body
}
Because our MyIterator was built from first principles, you have a deeper appreciation for what happens under the hood!

Subsection 4.6.4 Example: Summing Even Values & Using break

Let’s show a slightly more advanced for-each example—summing only the first few even values. Suppose we have:
int[] data = {2, 11, 4, 6, 9, 2};
int totalEven = 0;

for (int val : data) {
    if (val % 2 == 0) {
        totalEven += val;
    }
    // If we only want to sum the first 3 array elements total:
    // This won't help if we need to stop after the 3rd element. 
    // for-each doesn't give an index. 
}
System.out.println("Sum of even numbers: " + totalEven);
That sums all even numbers (2 + 4 + 6 + 2 = 14). However, if we needed to stop after the 3rd element in the array or skip certain positions, we’d realize for-each can’t easily do that. We’d have to add an external counter or break logic:
int count = 0;
int sumEvens = 0;
for (int val : data) {
    sumEvens += (val % 2 == 0) ? val : 0;
    count++;
    if (count == 3) { 
       break; // we processed 3 elements, let's stop
    }
}
System.out.println("Sum of even numbers in the FIRST 3 elements: " + sumEvens);
That works but starts feeling awkward. If you need to break frequently or refer to indexes, a classic for loop is often simpler.
Key takeaway: For-each loops are clean and short for “iterate all elements,” but less flexible when partial iteration or index-based operations are required.

Subsection 4.6.5 Interactive Exercises: Practicing For-Each Loops

Let’s put your new skills to the test! The following exercises reinforce how for-each loops can simplify your code for straightforward iteration tasks.

Checkpoint 4.6.2.

Exercise 1: Write a program that uses a for-each loop to compute and print the sum of the elements in the following array:
int[] values = {3, 7, 2, 5};
The expected output is: Sum is: 17

Checkpoint 4.6.3.

Exercise 2: Given the following array of names, use a for-each loop to print a personalized greeting for each name. For example, if the array contains {"Alice", "Bob", "Charlie"}, the output should be:
Hello, Alice
Hello, Bob
Hello, Charlie

Checkpoint 4.6.4.

Exercise 3: Can We Modify Each Element? Suppose we have int[] scores = {2, 4, 6}; and want to double each value. Try using a for-each loop, then print the array. Observe whether the final array actually changed or not. Then fix the code (hint: we must use a standard for loop or some trick).

Subsection 4.6.6 Summary: For-Each Loops

In this section, we explored the enhanced for loop—a concise, intuitive way to iterate over arrays. We learned that:
  • The for-each loop automatically retrieves each element without needing an index variable or boundary checks.
  • It’s syntactic sugar that conceals the repeated logic of hasNext() and next() calls.
  • Our custom iterator design parallels how for-each loops work under the hood.
  • For-each loops are best for “visit every element, forward only,” with minimal fuss. If you need random access, skipping, or partial iteration, you might revert to a traditional loop.
  • Reassigning the loop variable in a for-each does not alter the array. To modify array elements, you must use an indexed loop.
With this knowledge, you can write cleaner, more expressive loops in your Java programs when you just need to process each element. In the next chapter, we’ll delve deeper into classes and objects (including interfaces and inheritance) and see how the standard Iterator and Iterable interfaces power Java’s for-each loop across various collections—beyond just arrays.
You have attempted of activities on this page.