Explain Codes LogoExplain Codes Logo

How does the Java 'for each' loop work?

java
iterator
concurrentmodificationexception
collections
Nikita BarsukovbyNikita Barsukov·Nov 9, 2024
TLDR

A Java 'for each' loop is a handy way to iterate over elements in arrays and Iterable collections. The use of an internal Iterator eliminates explicit index tracking, resulting in cleaner code:

for (String item : Arrays.asList("Java", "Python", "JavaScript")) { System.out.println(item); // Print languages like there's no tomorrow! }

Here, item is assigned each value from the list successively, making it straightforward to access elements without the potential error-prone manual index handling.

Diving into Further Details

Cracking the Underlying Mechanism

In a for each loop, Java covertly uses an Iterator to traverse the collection. Suppose we perform the looping manually using an Iterator. In that case, it will look something like:

List<String> itemList = Arrays.asList("Java", "Python", "JavaScript"); Iterator<String> iterator = itemList.iterator(); while (iterator.hasNext()) { String item = iterator.next(); System.out.println(item); // Printing out languages like a whizzkid }

Knowing that Iterator underlies the for each loop, it becomes clear why this loop type can only go over Iterable objects. It also helps explain why certain modifications (such as removal of elements during iteration) aren't permitted, as they can lead to well-known errors like ConcurrentModificationException.

Amending Entries, Safe and Permissible

for each loops don't inherently support the removal of elements. To do that safely, you'll need to use an Iterator directly:

while (iterator.hasNext()) { if (iterator.next().equals("Python")) { iterator.remove(); // Farewell, Python. Nothing personal, but Java can be a little jealous. } }

This way, you're in full control of collection amendments, avoiding the hassles of index-based element removal.

Array vs Iterable Speed

When operating on primitive arrays, a conventional for loop is lightning-fast due to direct index access:

int[] numbers = {1, 2, 3}; for (int i = 0; i < numbers.length; i++) { System.out.println(numbers[i]); // Printing out the numbers like a counting pro }

However, with Iterable objects (like List<Integer>), a for each loop is more efficient than an indexed loop due to the boxing and unboxing overhead with the Integer type.

Efficiency with Index-based Collections

For collections that offer efficient indexed access (like ArrayList), both classic for loops and Iterator can perform similarly. Choosing the right tool for the task is crucial — random access is effective for indexed collections, whereas for each shines when indices aren't a concern, or when traversing collections with inefficient index-based access.

Custom Class Adventures in 'for each' Land

Implementing Iterable

Making your custom classes Iterable equips them with for each loop compatibility, improving their flexibility, and congruity with the Java collection framework:

public class CustomCollection<T> implements Iterable<T> { // ... Collection implementation code public Iterator<T> iterator() { // ... Now, go make an iterator here! } }

Once Iterable is implemented, vanquishing the complexity of iterating over your custom collection with a for each loop becomes a piece of cake.

The Iteration Barometer

Choosing the Right Iteration Method

The iteration method of choice boils down to the use case: indexed for loops for precision and speed with arrays, for each for ease of use and safety with Iterable collections, and explicit Iterator usage when modifications are needed. It's a delicate dance between manipulation needs and performance considerations.

Visualising the 'for each' Loop

You're the conductor of a train(🚂), checking every carriage:
Java Collection : [Carriage 🍎, Carriage 🍌, Carriage 🍒]

Appropriate 'for-each' code:
for (Fruit carriage : fruitTrain) {
   // Conductor inspecting each carriage (🍎, then 🍌, then 🍒)
}

Just like the conductor, the loop proceeds to the next carriage (element) using the train's (collection's) tracks (Iterator), making for a smooth, index-free ride!

Advancing Your Iteration Game

Scaring the 'ConcurrentModificationException' Ghost Away

Understanding Java's 'for each' loop entails acknowledging its limitations, especially related to concurrency. Using for each while modifying the collection (like adding or removing elements) can trigger ConcurrentModificationException. To exorcise this ghost, use the Iterator with the remove() method.

Custom Iteration Actions

When your iteration actions need to veer off the beaten path, or when additional operations during iteration are called for, then directly using Iterable interface and Iterator methods becomes the go-to approach.

Mastering Iterable

Diving into the Iterable interface is the prime step toward understanding for each loops. This knowledge prepares you to pick the right loop for the task—whether it's the simplicity of for each, the control of an explicit Iterator, or the precision of an indexed loop.