Java Iterators
An Iterator is a design pattern that provides a standard
way to traverse any Collection without exposing its internal structure. It's
essential for safely modifying collections during iteration.
Why Use Iterators?
- Uniform traversal: Same approach works for any Collection
- Safe removal: Only way to remove elements during iteration
- Encapsulation: Don't need to know internal structure
- Fail-fast: Detects concurrent modifications immediately
The Iterator Interface
public interface Iterator<E> {
boolean hasNext(); // Returns true if more elements exist
E next(); // Returns next element, advances cursor
void remove(); // Removes last element returned by next()
// Java 8+ default method
void forEachRemaining(Consumer<? super E> action);
}
Basic Usage
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
// Get iterator
Iterator<String> it = names.iterator();
// Classic iteration pattern
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}
// Java 8+ forEachRemaining
Iterator<String> it2 = names.iterator();
it2.forEachRemaining(name -> System.out.println(name));
Safe Removal During Iteration
This is the main reason to use Iterator over for-each loops:
List<Integer> numbers = new ArrayList<>(
Arrays.asList(1, 5, 12, 3, 8, 15, 7, 20)
);
// CORRECT: Using Iterator.remove()
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
Integer num = it.next();
if (num > 10) {
it.remove(); // Safe! Removes current element
}
}
// numbers is now [1, 5, 3, 8, 7]
// WRONG: Modifying during for-each
for (Integer num : numbers) {
if (num > 10) {
numbers.remove(num); // Throws ConcurrentModificationException!
}
}
ConcurrentModificationException: This occurs when a collection
is modified while iterating with for-each. Always use
Iterator.remove()
or Java 8+ removeIf() instead.
Output
Click Run to execute your code
ListIterator (For Lists Only)
ListIterator extends Iterator with bidirectional traversal and modification:
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
ListIterator<String> lit = list.listIterator();
// Forward traversal
while (lit.hasNext()) {
int index = lit.nextIndex();
String element = lit.next();
System.out.println(index + ": " + element);
}
// Backward traversal
while (lit.hasPrevious()) {
int index = lit.previousIndex();
String element = lit.previous();
System.out.println(index + ": " + element);
}
// Start from specific index
ListIterator<String> fromMiddle = list.listIterator(2); // Start at index 2
// Modify during iteration
ListIterator<String> modifier = list.listIterator();
while (modifier.hasNext()) {
String s = modifier.next();
if (s.equals("B")) {
modifier.set("B-modified"); // Replace current element
}
if (s.equals("C")) {
modifier.add("C-extra"); // Add after current element
}
}
Java 8+ Alternatives
List<Integer> numbers = new ArrayList<>(
Arrays.asList(1, 5, 12, 3, 8, 15, 7, 20)
);
// removeIf() - Cleaner than Iterator for simple removal
numbers.removeIf(n -> n > 10); // Same result as Iterator example
// Stream filter (creates new collection)
List<Integer> filtered = numbers.stream()
.filter(n -> n <= 10)
.collect(Collectors.toList());
// forEach with lambda
numbers.forEach(n -> System.out.println(n));
// replaceAll
numbers.replaceAll(n -> n * 2); // Double all values
When to Use Iterator vs For-Each
| Use Case | Best Choice |
|---|---|
| Simple traversal (read-only) | for-each loop |
| Need to remove elements | Iterator or removeIf() |
| Need current index | Traditional for loop or ListIterator |
| Bidirectional traversal | ListIterator |
| Add elements during iteration | ListIterator |
| Parallel processing | Stream API |
The Iterable Interface
Any class implementing Iterable can be used in for-each loops:
public class NumberRange implements Iterable<Integer> {
private final int start;
private final int end;
public NumberRange(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
private int current = start;
@Override
public boolean hasNext() {
return current <= end;
}
@Override
public Integer next() {
return current++;
}
};
}
}
// Now usable in for-each!
for (int num : new NumberRange(1, 5)) {
System.out.println(num); // 1, 2, 3, 4, 5
}
Summary
- Iterator provides safe, uniform collection traversal
- Use
Iterator.remove()to safely remove during iteration - ListIterator adds bidirectional traversal and modification
- For-each uses Iterator internally but doesn't allow removal
- Java 8+: Consider
removeIf(),forEach(), and Streams - Implement
Iterableto make custom classes for-each compatible
Enjoying these tutorials?