1. Overview
In Java programming, navigating through collections and arrays is crucial. In this article, we discuss two primary approaches using the Iterator interface and the forEach() method. We aim to understand the distinctions between them. Understanding the differences between these iteration techniques is key to efficient Java coding, whether for simplicity, or flexibility.
2. Iterator
Iterator is one of the tools that we can use to traverse a collection. It embodies the Iterator design pattern which provides a standard way to access elements sequentially without exposing the underlying collection’s representation.
Let’s see an example:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
// Perform complex operations
}
The Iterator shines in complex scenarios, allowing for detailed iteration control and the ability to modify the collection during iteration.
Let’s see another Iterator example:
Iterator it = list.iterator();
while (it.hasNext()) {
String item = it.next();
if (item.equals("unwanted")) {
it.remove(); // Safely remove item
}
}
However, its verbose nature can make the code less readable, especially with complex iteration logic.
3. forEach()
Starting with Java 8, the Iterable interface includes the forEach() method which performs a given action for each collection element.
Let’s see a forEach() Example:
list.forEach(item -> {
// Execute simple operations
System.out.println(item);
});
forEach() helps us to enhance code readability. This simplifies iteration with concise syntax and is ideal for straightforward traversals without collection modification.
forEach() doesn’t directly support modifying the collection it iterates over. Attempting to remove or add elements to the collection during a forEach() loop can result in a compilation error “local variables referenced from a lambda expression must be final or effectively final“:
list.forEach(item -> {
if (item.equals("unwanted")) {
// Direct removal will cause a compilation error
// list.remove(item);
}
});
The reason is the loop isn’t designed to be aware of any structural changes to the collection.
There is another technique to modify a collection during a forEach() iteration. We need to collect items in a separate list to modify the original list afterward. However, this can be less efficient and more cumbersome:
// Separate collection for items to be removed
List<String> toRemove = new ArrayList<>();
// Using forEach() to identify items to remove
list.forEach(item -> {
if (item.equals("unwanted")) {
toRemove.add(item);
}
});
// Removing the identified items from the original list
list.removeAll(toRemove);
4. Iterator vs forEach()
The performance difference between using an Iterator and the forEach() loop is minimal and shouldn’t be the primary factor in choosing one over the other.
Let’s see their comparison table here:
Loop element | Syntax | Modification Capability | Performance |
---|---|---|---|
Iterator | less readable | supports | nearly the same |
forEach() | more readable | doesn’t support | nearly the same |
The Iterator is our best choice if we require the flexibility to modify the collection dynamically. The forEach() loop is the more suitable option for scenarios emphasizing code clarity and simplicity.
5. Conclusion
In Java, the choice between using an Iterator and a forEach() loop for iterating over collections depends on the specific requirements of our project. Each method offers distinct advantages and comes with its own set of considerations, particularly when modifying collections during iteration, usability, and readability.
In this article, we reviewed the key points when deciding between Iterator and forEach() in Java. Understanding the strengths and limitations of each will enable us to write more efficient, maintainable, and readable Java code.
Finally, as always the full source code is available over on GitHub.