Java For-Each Loop

The for-each loop, also called the enhanced for loop, is a simplified way to visit every element in an array or in any object that implements Iterable, such as an ArrayList, a HashSet, or a LinkedList. Instead of manually tracking an index and comparing it against a length, you let Java hand you each element directly, which makes the code shorter and removes a whole class of off-by-one bugs. It was introduced in Java 5 specifically to make iteration read closer to plain English, and today it is the default choice whenever you do not need the index itself.

Overview / How It Works

A for-each loop is not a new kind of loop at the bytecode level — it is syntactic sugar that the compiler rewrites into an ordinary loop before your code is compiled to bytecode. What it rewrites to depends on what you are iterating over:

For an array, the compiler generates a classic index-based loop behind the scenes. It stores a reference to the array, checks the current index against array.length on every pass, reads array[index] into your loop variable, and increments the index. You never see this generated code, but understanding it explains why for-each is just as fast as a manual indexed loop for arrays — there is no hidden overhead.

For anything that implements java.lang.Iterable<T> (which includes every class in the Collections Framework — List, Set, Map.values(), and so on), the compiler instead calls the object’s iterator() method once, then repeatedly calls hasNext() to decide whether to continue and next() to fetch the next element. This is exactly the same pattern you would write by hand with an explicit Iterator, just without the boilerplate.

Because the for-each loop only exposes the value of each element, not its position, it cannot tell you the current index, cannot easily iterate backwards, and cannot safely skip or jump elements. It also cannot iterate two collections in lockstep unless you nest two separate loops or fall back to an indexed loop. Those limitations are the trade-off for its readability, and knowing them helps you decide when to reach for a traditional for loop instead.

Syntax

for (Type element : arrayOrIterable) {
    // use element here
}
Part Meaning
Type The declared type of each element (e.g. int, String, or a generic type like Employee). Must match, or be a supertype of, the array/collection’s element type.
element A new local variable created fresh on every iteration, holding the current value.
: Read as “in” — for (int n : numbers) means “for each int n in numbers”.
arrayOrIterable An array (int[], String[], etc.) or any object implementing Iterable<T>.

Examples

Example 1: Summing an int array

public class Main {
    public static void main(String[] args) {
        int[] numbers = {4, 8, 15, 16, 23, 42};
        int sum = 0;
        for (int num : numbers) {
            System.out.println("Value: " + num);
            sum += num;
        }
        System.out.println("Sum: " + sum);
    }
}

Output:

Value: 4
Value: 8
Value: 15
Value: 16
Value: 23
Value: 42
Sum: 108

Each pass through the loop assigns the next array element to num, prints it, and adds it to sum. There is no index variable to manage, and no risk of reading past the end of the array.

Example 2: Iterating a List and a 2D array

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Cherry");

        for (String fruit : fruits) {
            System.out.println(fruit.toUpperCase());
        }

        int[][] grid = {
            {1, 2, 3},
            {4, 5, 6}
        };

        int total = 0;
        for (int[] row : grid) {
            for (int value : row) {
                total += value;
            }
        }
        System.out.println("Grid total: " + total);
    }
}

Output:

APPLE
BANANA
CHERRY
Grid total: 21

The first loop shows for-each working with a List<String> exactly as it does with an array. The second part shows that a 2D array is really an array of arrays, so you nest two for-each loops: the outer one yields each int[] row, and the inner one yields each int inside that row.

Example 3: For-each on a custom Iterable

import java.util.Iterator;
import java.util.NoSuchElementException;

public class Main {
    static class Countdown implements Iterable<Integer> {
        private final int start;

        Countdown(int start) {
            this.start = start;
        }

        @Override
        public Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                private int current = start;

                @Override
                public boolean hasNext() {
                    return current >= 0;
                }

                @Override
                public Integer next() {
                    if (!hasNext()) {
                        throw new NoSuchElementException();
                    }
                    return current--;
                }
            };
        }
    }

    public static void main(String[] args) {
        Countdown countdown = new Countdown(5);
        for (int number : countdown) {
            System.out.println(number);
        }
        System.out.println("Liftoff!");
    }
}

Output:

5
4
3
2
1
0
Liftoff!

This example proves that for-each is not special-cased to built-in collections. Any class that implements Iterable<T> and supplies an Iterator<T> can be used in a for-each loop, because the compiler only ever needs iterator(), hasNext(), and next().

Under the Hood: What the Compiler Generates

It helps to see, in plain terms, what javac produces for each case:

For for (int num : numbers) where numbers is an array, the compiler effectively writes: store numbers in a hidden variable, initialize a hidden index to 0, loop while the index is less than the array’s length, assign numbers[index] to num at the top of each iteration, then increment the hidden index. Your num variable is a fresh copy of the value each time.

For for (String fruit : fruits) where fruits is a List<String>, the compiler effectively writes: call fruits.iterator() once to get an Iterator<String>, loop while hasNext() returns true, and assign the result of next() to fruit on each pass. The Iterator object itself keeps track of position internally — the for-each loop has no index of its own.

This is why for-each variables are effectively read-only copies: reassigning the loop variable inside the body changes only that local copy, not the underlying array slot or collection element (see the first Common Mistake below), and why removing elements from a List mid-iteration is dangerous — the Iterator notices the collection changed underneath it and throws an exception rather than silently producing wrong results.

Common Mistakes

Mistake 1: Assuming the loop variable modifies the array

public class Main {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};
        for (int n : numbers) {
            n = n * 10;
        }
        System.out.println(numbers[0] + ", " + numbers[1] + ", " + numbers[2]);
    }
}

Output:

1, 2, 3

Beginners often expect this to print 10, 20, 30. It does not, because n is a copy of each array value, not a reference back into the array. Assigning to n only changes the local copy. To actually mutate the array, use a traditional indexed loop: for (int i = 0; i < numbers.length; i++) { numbers[i] *= 10; }.

Mistake 2: Removing elements from a List while iterating with for-each

import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Ann");
        names.add("Bob");
        names.add("Cal");

        try {
            for (String name : names) {
                if (name.equals("Bob")) {
                    names.remove(name);
                }
            }
        } catch (ConcurrentModificationException e) {
            System.out.println("Caught: " + e.getClass().getSimpleName());
        }
    }
}

Output:

Caught: ConcurrentModificationException

Calling names.remove(...) directly on the list changes its internal modification count, and the hidden Iterator detects the mismatch on the next call to next() and throws ConcurrentModificationException to protect you from silently skipped elements or corrupted state. The fix is to either use an explicit Iterator and call iterator.remove(), or, more simply, use List.removeIf:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Ann");
        names.add("Bob");
        names.add("Cal");

        names.removeIf(name -> name.equals("Bob"));

        for (String name : names) {
            System.out.println(name);
        }
    }
}

Output:

Ann
Cal

removeIf handles the removal safely internally, so there is no live iterator to confuse, and the follow-up for-each loop simply prints what remains.

Best Practices

  • Prefer for-each whenever you only need each element’s value and not its index — it is clearer and less error-prone than a manual indexed loop.
  • Fall back to a traditional indexed for loop when you need the current index, need to iterate backwards, need to skip elements, or need to modify a primitive array in place.
  • Never add or remove elements from a List or Set directly inside a for-each loop over that same collection; use Iterator.remove(), removeIf, or build a new collection instead.
  • Declare the loop variable with the narrowest correct type; letting it be a supertype (e.g. Object for a List<String>) throws away useful compiler checking.
  • Remember the loop variable is a fresh copy each iteration — reassigning it never changes the source array or collection.
  • When iterating a Map, iterate map.entrySet() with a for-each loop rather than iterating keys and calling map.get(key) repeatedly; it avoids a lookup per entry.

Practice Exercises

  • Exercise 1: Given String[] words = {"sky", "ocean", "mountain", "cloud"};, use a for-each loop to print only the words with more than 4 characters.
  • Exercise 2: Create a List<Integer> containing 1 through 10. Use a for-each loop to compute and print the sum of only the even numbers. Expected output: 30.
  • Exercise 3: Given a Map<String, Integer> of product names to prices, use a for-each loop over entrySet() to print each product and its price on one line, formatted as name: $price.

Summary

  • The for-each loop iterates over every element of an array or any Iterable without managing an index manually.
  • For arrays, the compiler generates an indexed loop; for Iterable types, it generates calls to iterator(), hasNext(), and next().
  • The loop variable is a fresh, read-only copy each iteration — changing it never affects the source array or collection.
  • Adding or removing elements from a List/Set during a for-each loop over it throws ConcurrentModificationException; use Iterator.remove() or removeIf instead.
  • Choose for-each for readability when the index is not needed; choose an indexed loop when it is.