Iterator & Iterable
IntermediateImplement Iterable to enable enhanced for-each on your own classes, understand Iterator fail-fast behaviour, and use ListIterator for bidirectional traversal.
Overview
The Iterator pattern decouples traversal logic from the collection. Iterable<T> has one method: iterator(), which returns an Iterator<T>. Iterator<T> has hasNext(), next(), and optional remove(). Implementing Iterable on your class enables enhanced for-each syntax. Java's collection iterators are fail-fast — they throw ConcurrentModificationException if the collection is structurally modified during iteration (via any means other than the iterator's own remove).
Iterator & Iterable Contracts
Iterator contract: • hasNext() — returns true if more elements remain • next() — returns the next element, throws NoSuchElementException if none • remove() — optional; removes the last element returned by next()
Iterable contract: implement iterator() to return a fresh Iterator each time.
import java.util.Iterator;
import java.util.NoSuchElementException;
// Custom range that is Iterable — enables for-each
public class Range implements Iterable<Integer> {
private final int start;
private final int end; // exclusive
public Range(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
private int current = start;
@Override public boolean hasNext() { return current < end; }
@Override public Integer next() {
if (!hasNext()) throw new NoSuchElementException();
return current++;
}
// remove() not supported — default throws UnsupportedOperationException
};
}
public static void main(String[] args) {
Range range = new Range(1, 6);
// Enhanced for-each — works because Range is Iterable
for (int n : range) System.out.print(n + " "); // 1 2 3 4 5
System.out.println();
// Explicit iterator usage
Iterator<Integer> it = range.iterator();
while (it.hasNext()) System.out.print(it.next() + " ");
System.out.println();
// Stream from Iterable (via StreamSupport)
import java.util.stream.StreamSupport;
StreamSupport.stream(range.spliterator(), false)
.filter(n -> n % 2 == 0)
.forEach(System.out::print); // 2 4
}
}Fail-Fast & Safe Removal
ArrayList, HashMap, HashSet iterators are fail-fast — they track modCount and throw ConcurrentModificationException if you structurally modify the collection outside the iterator during iteration. Always use Iterator.remove() or Collection.removeIf() for safe in-loop removal.
import java.util.*;
public class IteratorSafety {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
// WRONG: remove() via list during for-each — throws CME
try {
for (Integer n : list) {
if (n % 2 == 0) list.remove(n); // structural modification!
}
} catch (ConcurrentModificationException e) {
System.out.println("CME caught");
}
// CORRECT 1: Iterator.remove()
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
if (it.next() % 2 == 0) it.remove(); // safe
}
System.out.println(list); // [1, 3, 5]
// CORRECT 2: removeIf (cleanest)
list = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
list.removeIf(n -> n % 2 == 0);
System.out.println(list); // [1, 3, 5]
// Map iteration
Map<String, Integer> map = new HashMap<>(Map.of("a",1,"b",2,"c",3));
map.entrySet().removeIf(e -> e.getValue() < 2); // safe bulk removal
System.out.println(map); // {b=2, c=3}
}
}ListIterator — Bidirectional Traversal
ListIterator extends Iterator and adds hasPrevious(), previous(), nextIndex(), previousIndex(), add(), and set(). It allows traversal in both directions and mutation during iteration — essential for efficient in-place modification of a LinkedList.
import java.util.*;
public class ListIteratorDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>(List.of("a","b","c","d","e"));
// ListIterator — bidirectional
ListIterator<String> lit = list.listIterator(list.size()); // start at end
System.out.print("Reverse: ");
while (lit.hasPrevious()) System.out.print(lit.previous() + " ");
System.out.println(); // e d c b a
// set() — replace last returned element
ListIterator<String> it2 = list.listIterator();
while (it2.hasNext()) {
String s = it2.next();
it2.set(s.toUpperCase()); // replace in-place
}
System.out.println(list); // [A, B, C, D, E]
// add() — insert at current position
ListIterator<String> it3 = list.listIterator();
it3.next(); // moves past A
it3.add("A+"); // inserts after A
System.out.println(list); // [A, A+, B, C, D, E]
}
}Key Points to Remember
- Implement Iterable<T> with iterator() to enable enhanced for-each on custom classes
- Iterator.remove() is the only safe way to remove during iteration — do not use collection.remove() in a loop
- removeIf(Predicate) is the cleanest bulk-removal approach — no CME risk
- Fail-fast iterators throw ConcurrentModificationException on concurrent structural modification
- ListIterator supports bidirectional traversal, set() and add() during iteration
- StreamSupport.stream(iterable.spliterator(), parallel) converts any Iterable to a Stream
Practice Iterator & Iterable in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat is the difference between Iterable and Iterator?
What is a fail-fast iterator? How do you avoid ConcurrentModificationException?
How do you safely remove elements from a list while iterating?
What is the difference between Iterator and ListIterator?
How do you implement Iterable on a custom class?
Ask Aria about Iterator & Iterable
Your personal AI tutor — ask anything about this concept