Iterator
An Iterator is Java’s universal cursor for walking through any collection — one element at a time — without exposing the collection’s internal structure. Whether you’re looping over an ArrayList, a HashSet, or a LinkedList, the same three-method API works everywhere.
Why Iterator Exists
Before Java 2 introduced the Collections Framework, every data structure had its own traversal mechanism (Enumeration, index-based loops, etc.). The Iterator interface unified all of that behind a single contract, and it adds one critical feature a plain for loop lacks: safe removal of elements while iterating.
Note: The enhanced for-each loop is syntactic sugar over
Iterator— the compiler rewrites it into an iterator loop automatically. See for-each Loop for that syntax.
The Iterator Interface
java.util.Iterator<E> declares three methods:
| Method | Description |
|---|---|
boolean hasNext() | Returns true if more elements remain |
E next() | Returns the next element and advances the cursor |
void remove() | Removes the element last returned by next() (optional operation) |
Java 8 added a default forEachRemaining(Consumer<? super E>) method that processes all remaining elements with a lambda.
Basic Usage
Obtain an iterator from any Collection via iterator(), then loop:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class BasicIterator {
public static void main(String[] args) {
List<String> languages = new ArrayList<>();
languages.add("Java");
languages.add("Python");
languages.add("Go");
Iterator<String> it = languages.iterator();
while (it.hasNext()) {
String lang = it.next();
System.out.println(lang);
}
}
}
Output:
Java
Python
Go
Always call hasNext() before next(). If you call next() when no elements remain, you get a NoSuchElementException.
Removing Elements While Iterating
This is the main reason to use an explicit Iterator over a for-each loop. Calling collection.remove() inside a for-each throws ConcurrentModificationException; calling iterator.remove() is perfectly safe:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SafeRemove {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
int n = it.next();
if (n % 2 == 0) {
it.remove(); // removes 2 and 4 safely
}
}
System.out.println(numbers);
}
}
Output:
[1, 3, 5]
Warning: You must call
next()at least once before callingremove(), otherwise you get anIllegalStateException. You also cannot callremove()twice in a row without an interveningnext().
forEachRemaining (Java 8+)
If you want to process the rest of the elements with a lambda after some earlier manual iteration, forEachRemaining is handy:
import java.util.List;
import java.util.Iterator;
public class ForEachRemaining {
public static void main(String[] args) {
List<String> items = List.of("A", "B", "C", "D");
Iterator<String> it = items.iterator();
// manually consume the first element
System.out.println("First: " + it.next());
// process the rest with a lambda
it.forEachRemaining(s -> System.out.println("Rest: " + s));
}
}
Output:
First: A
Rest: B
Rest: C
Rest: D
ListIterator — Bidirectional Traversal
For List implementations, you can upgrade to a ListIterator<E>, which extends Iterator with backward traversal and in-place replacement:
| Extra Method | Description |
|---|---|
boolean hasPrevious() | Returns true if there are elements behind the cursor |
E previous() | Moves backward and returns the element |
int nextIndex() | Index of the element that next() would return |
int previousIndex() | Index of the element that previous() would return |
void set(E e) | Replaces the last element returned by next() or previous() |
void add(E e) | Inserts an element before the element that next() would return |
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorDemo {
public static void main(String[] args) {
List<String> colors = new ArrayList<>();
colors.add("Red");
colors.add("Green");
colors.add("Blue");
ListIterator<String> lit = colors.listIterator();
// forward pass — uppercase each element
while (lit.hasNext()) {
String c = lit.next();
lit.set(c.toUpperCase());
}
// backward pass
System.out.print("Reversed: ");
while (lit.hasPrevious()) {
System.out.print(lit.previous() + " ");
}
}
}
Output:
Reversed: BLUE GREEN RED
Tip:
listIterator(int index)lets you start the cursor at any position rather than the beginning.
The Iterable Interface
Any class that wants to work with the for-each loop must implement java.lang.Iterable<T>, which requires a single method: Iterator<T> iterator(). Every class in the Collections Framework already does this.
You can make your own classes iterable too:
import java.util.Iterator;
public class Range implements Iterable<Integer> {
private final int start;
private final int end;
public Range(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
int current = start;
@Override public boolean hasNext() { return current < end; }
@Override public Integer next() { return current++; }
};
}
public static void main(String[] args) {
for (int n : new Range(1, 6)) {
System.out.print(n + " ");
}
}
}
Output:
1 2 3 4 5
Under the Hood
Fail-Fast Iterators
Most java.util collection iterators (e.g., ArrayList, HashMap) are fail-fast. Each collection maintains an internal modCount field that increments on every structural change (add, remove, resize). When you call iterator(), the iterator snapshots modCount into its own expectedModCount. On every next() or remove() call it checks:
if (modCount != expectedModCount) throw new ConcurrentModificationException();
This protects you from subtle bugs caused by accidental concurrent modification — but note it is a best-effort check, not a guaranteed thread-safety mechanism.
Fail-Safe Iterators
Iterators in java.util.concurrent collections (e.g., CopyOnWriteArrayList, ConcurrentHashMap) are fail-safe: they operate on a snapshot or use lock-free techniques and never throw ConcurrentModificationException. The trade-off is that changes made after the iterator was created may or may not be visible. See Concurrent Collections.
Bytecode Reality
The compiler transforms this for-each loop:
for (String s : list) { ... }
into approximately:
Iterator<String> $it = list.iterator();
while ($it.hasNext()) {
String s = $it.next();
...
}
This means every for-each over a Collection has the overhead of creating one Iterator object. For small loops this is negligible; the JIT typically inlines and eliminates the allocation entirely.
Iterator vs for-each vs index loop
| Approach | Bidirectional | Safe removal | Works on sets/maps | Overhead |
|---|---|---|---|---|
Index for loop | Yes (lists) | Yes (with care) | No | Minimal |
| for-each loop | No | No | Yes | One iterator object |
Iterator | No | Yes (it.remove()) | Yes | One iterator object |
ListIterator | Yes | Yes | Lists only | One iterator object |
Tip: Use a for-each loop for read-only traversal. Switch to an explicit
Iteratoronly when you need to remove elements during iteration.
Related Topics
- Collections Framework — the big picture of Java’s data structure hierarchy
- ArrayList — the most common collection you will iterate over
- for-each Loop — the syntactic sugar built on top of Iterator
- Comparable — sorting elements you iterate over
- Concurrent Collections — fail-safe iterators for multi-threaded code
- List Interface — where
ListIteratorlives and what it offers