Back to Blog
Java

Java Enhanced For Loop: Syntax, Behavior, and Limits

How Java's enhanced for loop behaves for arrays and Iterables, where its limitations matter, and when a classic for loop or stream is a better fit.

Java loopsfor-eachIterableJava collectionsJava iteration
Diagram showing a Java enhanced for loop iterating over a collection with a highlighted loop variable.

The Java enhanced for loop, also called the for-each loop, is a concise way to iterate over arrays and objects that implement Iterable. It was introduced in Java 5 and removes the need for explicit index management or iterator calls. The syntax is straightforward:

for (Type element : iterable) { // process element }

The Enhanced For Loop Syntax and Its Scope

The enhanced for loop works with two kinds of targets: arrays and objects that implement java.lang.Iterable. For arrays, the loop reads each element sequentially. For Iterable objects, it uses the iterator returned by iterator() and calls hasNext() and next() behind the scenes. The loop variable is read-only in the sense that reassigning it does not affect the underlying collection.

List<String> names = Arrays.asList("Ada", "Grace", "Linus"); for (String name : names) { System.out.println(name); }

This is equivalent to:

for (Iterator<String> it = names.iterator(); it.hasNext(); ) { String name = it.next(); System.out.println(name); }

The compiler generates the iterator-based version for Iterable targets. For arrays, it generates a simple indexed loop.

How the Enhanced For Loop Works with Arrays

When the target is an array, the enhanced for loop compiles to a traditional indexed loop. It reads each element in order, from index 0 to length-1. The loop variable receives a copy of the element, so modifying it has no effect on the array.

int[] numbers = {1, 2, 3}; for (int n : numbers) { n = n * 2; // does not change numbers } // numbers remains {1, 2, 3}

For reference types, the loop variable holds a reference to the same object, so mutating the object's state is possible, but reassigning the variable is not reflected in the array.

How the Enhanced For Loop Works with Iterables

For any object implementing Iterable, the enhanced for loop uses the iterator returned by the iterator() method. This means it works with all Collection types, custom iterables, and any class that provides an iterator. The loop automatically calls hasNext() before each iteration and next() to retrieve the element.

Set<Integer> ids = new HashSet<>(Arrays.asList(10, 20, 30)); for (Integer id : ids) { System.out.println(id); }

Because the loop relies on the iterator, the iteration order is determined by the collection's iterator implementation. For a HashSet, the order is unspecified; for a TreeSet, it is sorted; for a LinkedList, it is insertion order.

When the Enhanced For Loop Cannot Replace the Classic For Loop

The enhanced for loop hides the index and the iterator. That makes it unsuitable when you need the index, such as replacing an element in an array. It also cannot be used to iterate over multiple collections simultaneously or to iterate backwards.

If you need the index, use a classic indexed loop:

for (int i = 0; i < array.length; i++) { array[i] = array[i] * 2; }

Modifying Collections During Iteration

A common pitfall is attempting to remove elements from a collection while using the enhanced for loop. The loop uses the collection's iterator, and most java.util iterators are fail-fast. If the collection is structurally modified after the iterator is created, the loop's behavior is unreliable: it may throw a ConcurrentModificationException on a later call to next(), or it may silently skip elements.

List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c")); for (String item : items) { if (item.equals("b")) { items.remove(item); // unsafe: may throw or skip elements } }

To remove elements safely, use an explicit Iterator and call remove() on the iterator where supported, or use removeIf in Java 8+:

items.removeIf(item -> item.equals("b"));

The enhanced for loop is also not safe for adding elements during iteration for the same reason.

Performance and Runtime Cost of the Enhanced For Loop

For arrays, the enhanced for loop compiles to the same bytecode as a classic indexed loop, so there is no performance penalty. For Iterable objects, the loop uses the iterator, which calls hasNext() and next() for each step through the collection. In most applications, this overhead is negligible compared to the actual work inside the loop.

The iterator itself is usually cheap to create for standard collections such as ArrayList and LinkedList. If a custom Iterable performs expensive work in next(), that cost is in that method, not in the loop syntax.

If you are micro-optimizing, the enhanced for loop is generally as fast as an explicit iterator loop. The classic indexed loop can be faster for an ArrayList because it avoids the iterator's hasNext() and next() calls, but the difference is rarely measurable in real applications. Prefer the enhanced for loop for readability unless profiling shows a bottleneck.

Choosing Between Enhanced For Loop and Streams

Java 8 introduced streams, which provide a different way to process collections. Streams allow functional-style operations like filter, map, and collect. The enhanced for loop is imperative and works well when you need to perform side effects on each element or when the logic is simple.

// Enhanced for loop for (String name : names) { if (name.startsWith("A")) { System.out.println(name.toUpperCase()); } } // Stream equivalent names.stream() .filter(name -> name.startsWith("A")) .map(String::toUpperCase) .forEach(System.out::println);

Use the enhanced for loop when:

  • You are iterating over an array or Iterable and do not need the index.
  • You need to break or return early from the loop.
  • You need to modify a local variable outside the loop, which is awkward inside a lambda.
  • The iteration logic is simple and side-effect oriented.

Use streams when you need to chain transformations, work with parallel processing, or prefer a declarative style. Streams cannot easily break early, and they add overhead for simple loops. For many everyday iteration tasks, the enhanced for loop remains a readable and efficient choice.

Java Enhanced For Loop: Syntax, Examples, and Limitations | RYUSLOG DEV