Iterators in Java
When working with collections (like Lists, Sets, and Maps) in Java, you frequently need to traverse their elements. While standard for loops or enhanced for-each loops are common, Iterators provide a more powerful and safe mechanism to traverse and manipulate collections.
1. The Iterator Interface
The java.util.Iterator interface provides a standardized way to traverse elements in a collection, replacing the older Enumeration interface.
Core Methods
boolean hasNext(): Returnstrueif the iteration has more elements.E next(): Returns the next element in the iteration. ThrowsNoSuchElementExceptionif no more elements exist.void remove(): Removes from the underlying collection the last element returned by this iterator. This is the only safe way to modify a collection during iteration.
Basic Example
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
System.out.println(name);
// Safely remove "Bob"
if (name.equals("Bob")) {
iterator.remove();
}
}
System.out.println(names); // [Alice, Charlie]
2. The Iterable Interface
A class implementing java.lang.Iterable indicates that it can provide an Iterator. The Java Collections Framework interfaces (Collection, List, Set) extend Iterable.
Implementing Iterable allows an object to be the target of an enhanced for-each loop.
// Under the hood, the enhanced for-loop:
for (String name : names) {
System.out.println(name);
}
// Is compiled to essentially:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}
3. The ListIterator Interface
ListIterator is an extension of Iterator specifically designed for List collections (ArrayList, LinkedList, etc.).
Additional Capabilities
- Bidirectional Traversal: Navigate backwards using
hasPrevious()andprevious(). - Modification: Add elements using
add(E e)and replace elements usingset(E e). - Indexing: Get indices using
nextIndex()andpreviousIndex().
Example
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
ListIterator<String> listIt = list.listIterator();
// Move forward
while (listIt.hasNext()) {
System.out.println("Forward: " + listIt.next());
}
// Move backward and modify
while (listIt.hasPrevious()) {
String val = listIt.previous();
if (val.equals("B")) {
listIt.set("X"); // Replace "B" with "X"
}
}
System.out.println(list); // [A, X, C]
4. Fail-Fast vs. Fail-Safe Iterators
A critical concept for Java interviews and concurrent programming is understanding how iterators handle structural modifications to the collection while iterating.
Fail-Fast Iterators
- Behavior: If the collection is structurally modified (elements added or removed) at any time after the iterator is created, in any way except through the iterator's own
remove()oradd()methods, the iterator will throw aConcurrentModificationException. - How it works: They maintain a
modCount(modification count) variable inside the collection. The iterator remembers themodCountwhen created. On everynext()call, it compares its expectedmodCountwith the actual collectionmodCount. - Examples: Default iterators for
ArrayList,HashMap,HashSet.
Fail-Safe (Non-Fail-Fast) Iterators
- Behavior: These iterators do not throw
ConcurrentModificationExceptionif the collection is modified during iteration. - How it works: They typically operate on a clone/copy of the collection created at the time the iterator is instantiated. Thus, changes to the original collection are not reflected in the iterator.
- Examples: Iterators for concurrent collections like
CopyOnWriteArrayList,ConcurrentHashMap.
5. Summary and Best Practices
- Use Enhanced For-Loop for Read-Only: If you only need to read elements, use the enhanced
for-eachloop for cleaner syntax. - Use Iterator for Removal: If you need to remove elements while iterating, use an explicit
Iteratorand itsremove()method. Modifying the collection directly (e.g.,list.remove()) within afor-eachloop will cause aConcurrentModificationException. - Use ListIterator for Bidirectional or In-Place Updates: When working with Lists and needing to traverse backwards or replace elements.
- Be Aware of Concurrency: In multithreaded environments, standard collections are fail-fast and not thread-safe. Use classes from
java.util.concurrentif multiple threads will modify and iterate simultaneously.