Java Iterator vs Enhanced For: When to Use Each
java iterator vs enhanced for: Compare Java's Iterator interface with the enhanced for loop: syntax, removal behavior, stateful iteration, allocation cost, and when ea...
When deciding between java iterator vs enhanced for, the real question is what happens underneath the syntax. The enhanced for loop in Java is the default choice for most iteration tasks, but it is not a separate iteration mechanism. For any Iterable or array, the enhanced for loop is compiled into either an Iterator-based loop or an index-based loop. Understanding what happens underneath is the difference between writing iteration code that works and code that fails with ConcurrentModificationException at runtime.
What the Enhanced For Loop Does Internally
When you write:
for (String item : items) { System.out.println(item); }
the compiler translates this into an iterator-based loop when items is a Collection or any other Iterable:
for (Iterator<String> iterator = items.iterator(); iterator.hasNext(); ) { String item = iterator.next(); System.out.println(item); }
The iterator is created once, and the loop body runs as long as hasNext() returns true. When items is an array, the compiler produces an index-based loop instead:
for (int i = 0; i < items.length; i++) { String item = items[i]; System.out.println(item); }
This distinction matters because the two forms have different behavior when the collection is modified during iteration. The iterator-based form checks for concurrent modification; the array form does not.
When You Need the Iterator Object Directly
The enhanced for loop hides the iterator, which is fine when you only need to read each element once in sequence. You need the Iterator object when the loop control logic depends on the iterator state.
Consider a method that consumes elements until a condition is met:
Iterator<String> iterator = queue.iterator(); while (iterator.hasNext()) { String next = iterator.next(); if (next.equals("stop")) { break; } process(next); }
The same logic is awkward with the enhanced for loop because the loop variable is assigned before the body runs, and you cannot inspect the next element without consuming it. The explicit iterator also lets you pass the iterator to another method, which is impossible with the enhanced for loop because the loop variable is scoped to the loop body.
Removing Elements Safely During Iteration
The most common failure with the enhanced for loop is attempting to remove an element while iterating:
for (String item : list) { if (item.startsWith("temp")) { list.remove(item); } }
This throws ConcurrentModificationException for most Collection implementations because the collection's modification count changes while the iterator expects the structure to remain unchanged. The Iterator.remove() method is the safe path:
Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (item.startsWith("temp")) { iterator.remove(); } }
Iterator.remove() is the only removal operation that keeps the iterator's internal state consistent. It also works on LinkedList and other List implementations where indexed removal would be expensive. Note that Iterator.remove() is optional for some implementations and throws UnsupportedOperationException if the backing collection does not support element removal.
Iterating Two Collections in Lockstep
A second scenario where the explicit iterator is necessary is when you need to traverse two collections in parallel without using an index. The enhanced for loop cannot keep two loop variables in sync because each loop is independent:
Iterator<String> leftIterator = leftList.iterator(); Iterator<String> rightIterator = rightList.iterator(); while (leftIterator.hasNext() && rightIterator.hasNext()) { processPair(leftIterator.next(), rightIterator.next()); }
This pattern stops as soon as either collection is exhausted, which is the behavior you want for zipping two sequences of different lengths. An index-based loop requires an explicit bounds check on both collections and does not work cleanly when the collections are not List types.
Performance and Object Allocation
The enhanced for loop and the explicit iterator have nearly identical runtime cost because the compiler generates the same iterator-based code. The difference is that the enhanced for loop creates the iterator implicitly, while the explicit version creates it in your code. For a single pass over a collection, the allocation of one Iterator object is negligible in most applications.
The cost difference becomes visible when the iteration itself is the bottleneck, such as in a hot loop over a very large collection. A LinkedList iterator allocates a new node reference per next() call, while an ArrayList iterator returns elements from the backing array. Neither form avoids this cost; the enhanced for loop and the explicit iterator pay the same price. If you need indexed access to an ArrayList, an index-based loop avoids the iterator allocation entirely:
for (int i = 0; i < list.size(); i++) { process(list.get(i)); }
This is only faster for ArrayList and other random-access lists. For LinkedList, indexed access is O(n) per call, making the iterator-based form strictly better.
Decision Criteria for Iterator vs Enhanced For
| Criterion | Enhanced For | Explicit Iterator |
|---|---|---|
| Reading all elements in order | Yes | Yes |
| Removing elements during iteration | No (throws) | Yes via remove() |
Checking hasNext() before next() | Implicit | Explicit |
| Passing iteration state to another method | No | Yes |
| Iterating two collections in lockstep | No | Yes |
| Working with arrays | Index-based internally | Not applicable |
| Code readability | Higher | Lower |
Use the enhanced for loop when you read elements in order and never modify the collection. Use the explicit iterator when you need removal, stateful control, or parallel traversal. The two are not interchangeable in those cases, and choosing the wrong one produces a runtime exception or requires awkward workarounds.
Compatibility and Behavioral Notes
The enhanced for loop was introduced in Java 5 and works with any type that implements Iterable. Custom classes can implement Iterable and provide their own iterator() method, which the enhanced for loop will use. The explicit Iterator interface has the same requirement. If a custom Iterable returns null from iterator(), both the enhanced for loop and the explicit version throw NullPointerException; there is no difference in that behavior.
One subtle difference is that the enhanced for loop cannot be used with a null collection reference, while the explicit iterator can check for null before calling iterator(). This is rarely a reason to prefer the explicit form, but it is a real behavioral difference when defensive null handling matters.