☕ Java

Enhanced for Loop

The enhanced for loop — also called the for-each loop — was introduced in Java 5 to provide a clean, readable syntax for iterating over arrays and any class that implements Iterable. It eliminates index management and iterator boilerplate entirely. The developer declares what each element is and what to do with it, without concerning themselves with how the iteration mechanism works. This entry covers syntax, the Iterable contract, limitations compared to the indexed loop, modification restrictions, and how for-each maps to iterators under the hood.

Syntax and the Iterable Contract

The enhanced for loop header declares a loop variable (the element type and name) and an expression that evaluates to an array or an Iterable. For each element in the collection or array, the variable is bound to that element and the body executes. The loop exits after the last element has been processed. The loop works with two categories of targets: arrays (any type) and objects whose class implements java.lang.Iterable<T>. The Iterable interface has a single method — iterator() — that returns an Iterator<T>. The Iterator provides hasNext() and next() to drive the loop. Every collection in the Java Collections Framework (List, Set, Queue, Deque, Map.keySet(), Map.values(), Map.entrySet()) implements Iterable, as does any custom class that provides iterator(). Implementing Iterable in your own class immediately enables it to work with the enhanced for loop.
Java
// ── Syntax ────────────────────────────────────────────────────────────
for (ElementType variable : arrayOrIterable) {
    // body — variable is bound to each element in turn
}

// ── Iterating an array ────────────────────────────────────────────────
int[] numbers = {10, 20, 30, 40, 50};

for (int num : numbers) {
    System.out.println(num);   // 10  20  30  40  50
}

// ── Iterating a List ──────────────────────────────────────────────────
List<String> names = List.of("Alice", "Bob", "Carol");

for (String name : names) {
    System.out.println("Hello, " + name);
}

// ── Iterating a Set ───────────────────────────────────────────────────
Set<String> roles = Set.of("USER", "ADMIN", "MANAGER");

for (String role : roles) {
    System.out.println("Role: " + role);
    // Order not guaranteed for HashSet
}

// ── Iterating Map entries ─────────────────────────────────────────────
Map<String, Integer> scores = Map.of(
    "Alice", 95, "Bob", 87, "Carol", 92);

for (Map.Entry<String, Integer> entry : scores.entrySet()) {
    System.out.printf("%s: %d%n",
        entry.getKey(), entry.getValue());
}

// ── What the compiler generates ──────────────────────────────────────
// The enhanced for loop over an Iterable compiles to:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
    String name = it.next();
    // body
}
// The enhanced for loop over an array compiles to a standard index loop:
for (int i = 0; i < numbers.length; i++) {
    int num = numbers[i];
    // body
}

Readability and Intent

The enhanced for loop's primary virtue is clarity of intent. An indexed for loop says "iterate from 0 to n-1 and access each element by index." An enhanced for loop says "do something for each element." The second is almost always the correct statement of intent when iterating a collection for reading or processing. When reviewing code, an indexed for loop signals that the index itself matters — the position is being used for something other than element access. An enhanced for loop signals that only the elements matter. This convention carries information to the reader about the purpose of the loop, which is valuable documentation embedded in the code structure itself.
Java
// ── Readability comparison ───────────────────────────────────────────

// Indexed loop — index is only used to access the element
// (signal: "I need the index" — but index is not used for anything else)
List<String> fruits = List.of("Apple", "Banana", "Cherry");
for (int i = 0; i < fruits.size(); i++) {
    System.out.println(fruits.get(i).toLowerCase());
}

// Enhanced for loop — cleaner, intent is clear
for (String fruit : fruits) {
    System.out.println(fruit.toLowerCase());
}

// ── Processing collections ────────────────────────────────────────────
List<Order> orders = orderRepo.findAll();

// Compute total revenue
double totalRevenue = 0;
for (Order order : orders) {
    totalRevenue += order.getTotal().doubleValue();
}

// Collect IDs
List<Long> orderIds = new ArrayList<>();
for (Order order : orders) {
    orderIds.add(order.getId());
}

// Find first order over £100
Order largeOrder = null;
for (Order order : orders) {
    if (order.getTotal().compareTo(new BigDecimal("100")) > 0) {
        largeOrder = order;
        break;
    }
}

// ── 2D array — nested enhanced for ───────────────────────────────────
int[][] grid = {{1,2,3},{4,5,6},{7,8,9}};

for (int[] row : grid) {
    for (int cell : row) {
        System.out.printf("%3d", cell);
    }
    System.out.println();
}
// Output:
//   1  2  3
//   4  5  6
//   7  8  9

Limitations — When NOT to Use Enhanced for

The enhanced for loop's simplicity comes from hiding the iteration mechanism. That hiding has costs: you cannot access the current index, you cannot iterate backwards, you cannot modify the collection size, and you cannot efficiently iterate multiple collections simultaneously. Any time you need these capabilities, the indexed loop or explicit Iterator is required. The loop variable is a copy of the reference (for objects) or the value (for primitives). Assigning to it reassigns the copy, not the original array element. For primitives in arrays this means in-place modification is impossible through the enhanced for loop — a fact that surprises many developers.
Java
// ── Limitation 1: Cannot access the index ────────────────────────────
List<String> items = List.of("a", "b", "c");

// WRONG — no index available
for (String item : items) {
    System.out.println(??? + ": " + item);   // no index
}

// Use indexed loop when index matters:
for (int i = 0; i < items.size(); i++) {
    System.out.println(i + ": " + items.get(i));   // 0:a  1:b  2:c
}

// ── Limitation 2: Cannot modify array elements ────────────────────────
int[] numbers = {1, 2, 3, 4, 5};

// WRONG — assigns to local copy, original array unchanged
for (int num : numbers) {
    num *= 2;   // modifies the copy, NOT numbers[i]
}
System.out.println(Arrays.toString(numbers));   // [1, 2, 3, 4, 5]

// Use indexed loop to modify in place:
for (int i = 0; i < numbers.length; i++) {
    numbers[i] *= 2;   // modifies the actual array element
}
System.out.println(Arrays.toString(numbers));   // [2, 4, 6, 8, 10]

// ── Limitation 3: Cannot remove from a List during iteration ─────────
List<String> names = new ArrayList<>(
    List.of("Alice", "Bob", "Carol", "Dave"));

// WRONG — throws ConcurrentModificationException
for (String name : names) {
    if (name.startsWith("C")) {
        names.remove(name);   // ← ConcurrentModificationException
    }
}

// CORRECT — use Iterator.remove() or removeIf()
names.removeIf(name -> name.startsWith("C"));
// or
Iterator<String> it = names.iterator();
while (it.hasNext()) {
    if (it.next().startsWith("C")) it.remove();
}

// ── Limitation 4: Cannot iterate in reverse ──────────────────────────
// Enhanced for always goes forward
// Use indexed loop for reverse:
for (int i = numbers.length - 1; i >= 0; i--) {
    System.out.print(numbers[i] + " ");
}

// ── Limitation 5: Cannot iterate two collections simultaneously ───────
// Use indexed loop or zip them first:
List<String> keys   = List.of("a", "b", "c");
List<Integer> vals  = List.of(1,   2,   3);

for (int i = 0; i < keys.size(); i++) {
    System.out.println(keys.get(i) + "=" + vals.get(i));
}

Custom Iterable Classes

Any class that implements Iterable<T> immediately works with the enhanced for loop. This is one of Java's most elegant extensibility points — a single method implementation gives the class the full syntactic support of the language's iteration facility. Custom Iterable is useful for domain-specific collections, range generators, lazy sequences, and any object that represents a traversable structure. Implementing Iterable correctly requires implementing hasNext(), next(), and optionally remove() on the returned Iterator. The Iterator must maintain its own state — typically a current position — so each call to iterator() returns a fresh, independent Iterator.
Java
// ── Custom range Iterable ─────────────────────────────────────────────
public class Range implements Iterable<Integer> {

    private final int start;
    private final int end;
    private final int step;

    public Range(int start, int end, int step) {
        this.start = start;
        this.end   = end;
        this.step  = step;
    }

    public static Range of(int start, int end) {
        return new Range(start, end, 1);
    }

    public static Range of(int start, int end, int step) {
        return new Range(start, end, step);
    }

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

            @Override
            public boolean hasNext() {
                return step > 0 ? current < end
                                : current > end;
            }

            @Override
            public Integer next() {
                if (!hasNext()) throw new NoSuchElementException();
                int value = current;
                current += step;
                return value;
            }
        };
    }
}

// ── Usage — works directly in enhanced for ────────────────────────────
for (int i : Range.of(0, 10)) {
    System.out.print(i + " ");   // 0 1 2 3 4 5 6 7 8 9
}

for (int i : Range.of(0, 20, 3)) {
    System.out.print(i + " ");   // 0 3 6 9 12 15 18
}

for (int i : Range.of(10, 0, -2)) {
    System.out.print(i + " ");   // 10 8 6 4 2
}

// ── Custom tree node Iterable (in-order traversal) ────────────────────
public class BinaryTree<T extends Comparable<T>>
        implements Iterable<T> {

    private Node<T> root;

    @Override
    public Iterator<T> iterator() {
        // In-order traversal using a stack
        return new Iterator<>() {
            private final Deque<Node<T>> stack = new ArrayDeque<>();
            { pushLeft(root); }   // initialise with leftmost path

            private void pushLeft(Node<T> node) {
                while (node != null) {
                    stack.push(node);
                    node = node.left;
                }
            }

            @Override
            public boolean hasNext() {
                return !stack.isEmpty();
            }

            @Override
            public T next() {
                Node<T> node = stack.pop();
                pushLeft(node.right);
                return node.value;
            }
        };
    }
}

// Usage:
BinaryTree<Integer> tree = buildTree();
for (int value : tree) {
    System.out.print(value + " ");   // sorted in-order output
}

Enhanced for with Records, Streams, and Var

Modern Java features compose naturally with the enhanced for loop. Records make clean data carriers for iteration. The var keyword (Java 10+) infers the element type, reducing verbosity in deeply typed generics. Streams are not Iterable — they cannot be used directly in a for-each — but converting a stream to a collection or using a custom Iterable bridge enables stream-generated sequences to be consumed with the familiar for-each syntax.
Java
// ── var — type inference in enhanced for ────────────────────────────
// Java 10+: var infers the element type
List<Map.Entry<String, List<Order>>> entries =
    new ArrayList<>(ordersByCustomer.entrySet());

// Without var — verbose type in loop variable
for (Map.Entry<String, List<Order>> entry : entries) {
    String      customer = entry.getKey();
    List<Order> orders   = entry.getValue();
}

// With var — cleaner
for (var entry : entries) {
    var customer = entry.getKey();    // String
    var orders   = entry.getValue();  // List<Order>
    System.out.println(customer + ": " + orders.size());
}

// ── Records as loop elements ──────────────────────────────────────────
record Point(int x, int y) {}
record LineSegment(Point start, Point end) {}

List<Point> polygon = List.of(
    new Point(0, 0), new Point(3, 0),
    new Point(3, 4), new Point(0, 4));

double perimeter = 0;
for (var point : polygon) {
    System.out.println("Vertex: " + point);
}

// ── Stream is NOT Iterable — cannot use directly ──────────────────────
Stream<String> stream = names.stream().filter(n -> n.length() > 3);

// WRONG — Stream does not implement Iterable
// for (String name : stream) { ... }  // compile error

// CORRECT option 1 — collect first
for (String name : stream.toList()) {
    System.out.println(name);
}

// CORRECT option 2Iterable bridge (lazy, single-use)
Iterable<String> iterable = stream::iterator;
for (String name : iterable) {
    System.out.println(name);
}

// ── Choosing between enhanced for and streams ──────────────────────────
// for-each: simple iteration, imperative logic, break/continue needed,
//           side effects (e.g. accumulating into an external structure)
//
// Stream:   pipeline transformations, map/filter/reduce,
//           parallel processing, functional style, no break needed

Related Topics in Control Statements