☕ Java

Java Iterator Interface

A complete guide to the Java Iterator interface — hasNext, next, remove, ListIterator, fail-fast vs fail-safe iterators, ConcurrentModificationException causes and fixes, custom Iterator implementation, Iterator with List, Set, and Map, and best practices with real-world examples.

📅

Last Updated

March 2026

⏱️

Read Time

17 min

🎯

Level

Beginner to Intermediate

What is the Java Iterator Interface?

The Iterator interface (java.util.Iterator<E>) is Java's universal cursor for traversing collections. It provides a standardised, collection-agnostic way to visit every element of any Collection — whether it is an ArrayList, HashSet, LinkedList, or TreeSet — one element at a time, without knowing or caring about the collection's internal structure.

Think of an Iterator as a bookmark that moves forward through a book. You ask it: "Is there a next page?" (hasNext()) and "Give me that page" (next()). You can also "tear out the current page" (remove()). The bookmark knows nothing about whether the book is hardcover or paperback (ArrayList vs LinkedList) — it just moves forward.

Iterator was introduced in Java 1.2 as the successor to the legacy Enumeration interface. It is also the engine behind Java's for-each loop — every time you write for (String s : list), the JVM internally calls list.iterator() and uses its hasNext() and next() methods. Understanding Iterator means understanding how the for-each loop actually works.

Iterator Syntax and Methods

The Iterator interface is obtained from any Collection by calling collection.iterator(). It defines three methods — all of which are used in the classic while-loop traversal pattern.

☕ JavaIterator Interface Definition
// java.util.Iterator<E> — the full interface
public interface Iterator<E> {

    // Returns true if the iteration has more elements
    boolean hasNext();

    // Returns the next element in the iteration
    // Throws NoSuchElementException if no more elements
    E next();

    // Removes the last element returned by next() from the collection
    // Throws IllegalStateException if next() has not been called yet
    // Throws UnsupportedOperationException if remove is not supported
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    // Java 8+ — performs action on each remaining element
    default void forEachRemaining(Consumer<? super E> action) {
        while (hasNext()) action.accept(next());
    }
}
☕ JavaIterator — First Program
import java.util.*;

public class IteratorIntro {
    public static void main(String[] args) {

        List<String> languages = new ArrayList<>();
        languages.add("Java");
        languages.add("Python");
        languages.add("Kotlin");
        languages.add("Scala");
        languages.add("Groovy");

        // Obtain iterator from the collection
        Iterator<String> it = languages.iterator();

        System.out.println("Iterating with Iterator:");
        while (it.hasNext()) {           // check if element exists
            String lang = it.next();     // retrieve next element
            System.out.println("  -> " + lang);
        }

        // Java 8+ — forEachRemaining
        System.out.println("\nforEachRemaining:");
        Iterator<String> it2 = languages.iterator();
        it2.forEachRemaining(lang -> System.out.println("  ** " + lang));
    }
}

Output

Iterating with Iterator: -> Java -> Python -> Kotlin -> Scala -> Groovy forEachRemaining: ** Java ** Python ** Kotlin ** Scala ** Groovy

Why Use Iterator?

With for-each loops available since Java 5, beginners often ask why Iterator is still needed. Here are the concrete reasons Iterator remains essential:

  • 🗑️ Safe Element Removal During Traversal — This is the single most important reason. Removing elements inside a for-each loop throws ConcurrentModificationException. Iterator.remove() is the only safe way to delete elements while iterating — it removes the last element returned by next() without triggering the exception.

  • 🔗 Works on Any Collection — Iterator is a universal interface. The same traversal code works on ArrayList, LinkedList, HashSet, TreeSet, or any custom Collection — no type-specific code needed. This is the Iterator Design Pattern in action.

  • ⚡ Powers the for-each Loop Internally — The for-each loop is syntactic sugar for Iterator. Every class that implements Iterable<T> (which returns an Iterator) supports for-each. Understanding Iterator explains how for-each actually works under the hood.

  • 📍 Conditional Filtering and Processing — Iterator's hasNext/next gives you precise control over traversal — skip, peek, and process elements conditionally in ways that for-each syntax does not allow without awkward workarounds.

  • 🔄 Partial Iteration — Iterator allows stopping mid-collection. You can break out of the while loop early (e.g., after finding a match) and resume later by keeping a reference to the same iterator object.

  • 🏗️ Foundation for ListIterator — ListIterator extends Iterator with bidirectional traversal and modification capabilities. Understanding Iterator is the prerequisite for understanding ListIterator's more powerful feature set.

Iterator vs ListIterator vs Enumeration vs for-each

Java offers four ways to traverse a collection. Each has distinct capabilities and appropriate use cases:

FeatureIteratorListIteratorEnumerationfor-each loop
IntroducedJava 1.2Java 1.2Java 1.0 (legacy)Java 5
Works onAny CollectionList onlyVector, Stack, Hashtable, PropertiesAny Iterable
DirectionForward onlyForward + BackwardForward onlyForward only
hasNext / next✅ hasNext(), next()✅ hasNext(), next()✅ hasMoreElements(), nextElement()Internal (hidden)
Remove element✅ remove()✅ remove()❌ Not supported❌ Throws ConcurrentModificationException
Add element❌ Not supported✅ add(E e)❌ Not supported❌ Not supported
Replace element❌ Not supported✅ set(E e)❌ Not supported❌ Not supported
Index access❌ No✅ nextIndex(), previousIndex()❌ No❌ No
Null safe?✅ Yes✅ Yes✅ Yes✅ Yes
Thread-safe?❌ Fail-fast❌ Fail-fast❌ Fail-fast (Vector)❌ Fail-fast
Best use caseSafe removal during traversalBidirectional + modify in placeLegacy API compatibilityClean read-only traversal

Rule of thumb: Use for-each for clean read-only traversal. Use Iterator when you need to remove elements during traversal. Use ListIterator when you need to go backwards, add, or replace elements in a List. Use Enumeration only when interacting with legacy APIs (Properties, Vector) that require it.

Iterator in the Java Collections Hierarchy

Iterator sits at the intersection of two key hierarchies — the Iterator interface hierarchy and the Iterable interface that every Collection implements.

java.lang.Iterable<T> (interface)
iterator() — returns Iterator<T>forEach(Consumer)spliterator()Implemented by: Collection, and any custom class supporting for-each
java.util.Iterator<E> (interface)
hasNext() — booleannext() — Eremove() — default voidforEachRemaining(Consumer) — default void (Java 8+)
java.util.ListIterator<E> (interface — extends Iterator<E>)
Adds: hasPrevious(), previous(), nextIndex(), previousIndex(), add(E), set(E)
Concrete Iterator Implementations (inner classes)
ArrayList.Itr — fail-fast IteratorArrayList.ListItr — fail-fast ListIteratorLinkedList.ListItr — fail-fast ListIteratorHashMap.KeyIterator — fail-fast Iterator for keySet()HashMap.ValueIterator — fail-fast Iterator for values()HashMap.EntryIterator — fail-fast Iterator for entrySet()CopyOnWriteArrayList.COWIterator — fail-safe IteratorConcurrentHashMap.KeyIterator — weakly consistent Iterator

Architecture Diagram

Key design insight: Iterable is the capability interface — it says "I can produce an Iterator". Iterator is the cursor itself. A class implementing Iterable enables for-each support. A class implementing Iterator defines how traversal happens. These two roles are deliberately separated so a collection can produce multiple independent iterators simultaneously.

Iterator and ListIterator Methods — Complete Reference

Here is the complete method reference for both Iterator and ListIterator, including the default methods added in Java 8:

MethodInterfaceDescriptionThrows
hasNext()IteratorReturns true if iteration has more elements going forward
next()IteratorReturns the next element and advances the cursorNoSuchElementException if no more elements
remove()IteratorRemoves the last element returned by next() from the underlying collectionIllegalStateException if next() not called; UnsupportedOperationException if not supported
forEachRemaining(Consumer)Iterator (Java 8+)Performs given action on each remaining element until exhausted or action throwsNullPointerException if action is null
hasPrevious()ListIteratorReturns true if iteration has more elements going backward
previous()ListIteratorReturns the previous element and moves cursor backwardNoSuchElementException if no previous element
nextIndex()ListIteratorReturns index of element that would be returned by next()
previousIndex()ListIteratorReturns index of element that would be returned by previous()
add(E e)ListIteratorInserts element immediately before element that would be returned by next()UnsupportedOperationException, ClassCastException, IllegalArgumentException
set(E e)ListIteratorReplaces the last element returned by next() or previous()IllegalStateException if neither next() nor previous() called; UnsupportedOperationException if not supported

Iterator with List, Set, and Map

Iterator works uniformly across all Collection types. Here is a comprehensive demo of Iterator with ArrayList, HashSet, and HashMap — including safe removal.

☕ JavaIterator with List — Safe Removal
import java.util.*;

public class IteratorWithList {
    public static void main(String[] args) {

        List<Integer> numbers = new ArrayList<>(
            Arrays.asList(10, 25, 3, 47, 8, 62, 15, 91, 4, 33));
        System.out.println("Original : " + numbers);

        // Remove all odd numbers using Iterator.remove()
        Iterator<Integer> it = numbers.iterator();
        while (it.hasNext()) {
            int n = it.next();
            if (n % 2 != 0) {
                it.remove();  // safe — no ConcurrentModificationException
            }
        }
        System.out.println("Evens only: " + numbers);

        // Partial traversal — stop after finding first element > 20
        List<Integer> scores = new ArrayList<>(
            Arrays.asList(5, 18, 33, 72, 11, 90));
        Iterator<Integer> it2 = scores.iterator();
        while (it2.hasNext()) {
            int s = it2.next();
            if (s > 20) {
                System.out.println("First score > 20: " + s);
                break;  // stop mid-collection
            }
        }
    }
}

Output

Original : [10, 25, 3, 47, 8, 62, 15, 91, 4, 33] Evens only: [10, 8, 62, 4] First score > 20: 33
☕ JavaIterator with Set and Map
import java.util.*;

public class IteratorSetMap {
    public static void main(String[] args) {

        // --- Iterator with HashSet ---
        Set<String> cities = new HashSet<>(
            Arrays.asList("Delhi", "Mumbai", "Pune", "Hyderabad", "Chennai"));

        System.out.println("Cities Set:");
        Iterator<String> setIt = cities.iterator();
        while (setIt.hasNext()) {
            System.out.print("  " + setIt.next());
        }

        // Remove cities with length < 5
        setIt = cities.iterator();
        while (setIt.hasNext()) {
            if (setIt.next().length() < 5) setIt.remove();
        }
        System.out.println("\nAfter removing short names: " + cities);

        // --- Iterator with Map (via entrySet) ---
        Map<String, Integer> stock = new LinkedHashMap<>();
        stock.put("Apples",  50);
        stock.put("Bananas", 0);
        stock.put("Mangoes", 30);
        stock.put("Grapes",  0);
        stock.put("Oranges", 15);

        System.out.println("\nOriginal stock: " + stock);

        // Remove out-of-stock items using Iterator on entrySet
        Iterator<Map.Entry<String, Integer>> mapIt = stock.entrySet().iterator();
        while (mapIt.hasNext()) {
            Map.Entry<String, Integer> entry = mapIt.next();
            if (entry.getValue() == 0) {
                mapIt.remove();  // safe removal from map via iterator
            }
        }
        System.out.println("In-stock only: " + stock);
    }
}

Output

Cities Set: Chennai Delhi Pune Mumbai Hyderabad After removing short names: [Delhi, Mumbai, Chennai, Hyderabad] Original stock: {Apples=50, Bananas=0, Mangoes=30, Grapes=0, Oranges=15} In-stock only: {Apples=50, Mangoes=30, Oranges=15}

ListIterator — Bidirectional Traversal and Modification

ListIterator extends Iterator with six additional methods that enable backward traversal, in-place replacement, and element insertion during iteration. It is only available on List implementations (ArrayList, LinkedList).

☕ JavaListIterator — All Capabilities Demo
import java.util.*;

public class ListIteratorDemo {
    public static void main(String[] args) {

        List<String> days = new ArrayList<>(
            Arrays.asList("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"));

        // --- Forward traversal with index ---
        System.out.println("Forward traversal:");
        ListIterator<String> lit = days.listIterator();
        while (lit.hasNext()) {
            System.out.println("  [" + lit.nextIndex() + "] " + lit.next());
        }

        // --- Backward traversal ---
        System.out.println("\nBackward traversal:");
        while (lit.hasPrevious()) {
            System.out.println("  [" + lit.previousIndex() + "] " + lit.previous());
        }

        // --- set() — replace elements in place ---
        System.out.println("\nReplace weekdays with short names:");
        ListIterator<String> setLit = days.listIterator();
        while (setLit.hasNext()) {
            String day = setLit.next();
            setLit.set(day.substring(0, 3).toUpperCase()); // Mon, Tue ...
        }
        System.out.println("  " + days);

        // --- add() — insert between elements ---
        System.out.println("\nInsert '--' between each day:");
        List<String> spaced = new ArrayList<>(days);
        ListIterator<String> addLit = spaced.listIterator();
        boolean first = true;
        while (addLit.hasNext()) {
            addLit.next();
            if (!first) { addLit.previous(); addLit.add("--"); addLit.next(); }
            first = false;
        }
        System.out.println("  " + spaced);

        // --- start from specific index ---
        System.out.println("\nStart from index 2:");
        ListIterator<String> fromIdx = days.listIterator(2);
        while (fromIdx.hasNext()) {
            System.out.print("  " + fromIdx.next());
        }
    }
}

Output

Forward traversal: [0] Monday [1] Tuesday [2] Wednesday [3] Thursday [4] Friday Backward traversal: [4] Friday [3] Thursday [2] Wednesday [1] Tuesday [0] Monday Replace weekdays with short names: [MON, TUE, WED, THU, FRI] Insert '--' between each day: [MON, --, TUE, --, WED, --, THU, --, FRI] Start from index 2: WED THU FRI

Fail-Fast vs Fail-Safe Iterators

One of the most important and interview-critical concepts around iterators is the difference between fail-fast and fail-safe behaviour. This determines what happens when a collection is modified while its iterator is active.

FeatureFail-Fast IteratorFail-Safe Iterator
Behaviour on modificationThrows ConcurrentModificationException immediatelyNever throws; continues safely
Detection mechanismmodCount check at each next() callOperates on a snapshot or weakly consistent view
Operates onOriginal collection directlyCopy of collection (or weakly consistent view)
Reflects live changes?Yes — until modification is detectedNo — may not reflect changes made after iterator creation
Memory overheadNone — no copyHigher — copy of data (CopyOnWriteArrayList)
Thread-safe?❌ Not safe for concurrent modification✅ Safe for concurrent read+write
ExamplesArrayList, HashMap, HashSet, LinkedList, TreeMapCopyOnWriteArrayList, ConcurrentHashMap, CopyOnWriteArraySet
When to useSingle-threaded code — fast, no overheadMulti-threaded code — safety over performance
☕ JavaFail-Fast vs Fail-Safe — Code Comparison
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

public class FailFastVsFailSafe {
    public static void main(String[] args) {

        // --- FAIL-FAST: ArrayList Iterator ---
        System.out.println("--- Fail-Fast (ArrayList) ---");
        List<String> failFastList = new ArrayList<>(
            Arrays.asList("Alpha", "Beta", "Gamma", "Delta"));

        Iterator<String> ffIt = failFastList.iterator();
        try {
            while (ffIt.hasNext()) {
                String s = ffIt.next();
                System.out.println("  Read: " + s);
                if (s.equals("Beta")) {
                    failFastList.add("Epsilon"); // direct modification!
                }
            }
        } catch (ConcurrentModificationException e) {
            System.out.println("  ConcurrentModificationException caught!");
        }

        // --- FAIL-SAFE: CopyOnWriteArrayList Iterator ---
        System.out.println("\n--- Fail-Safe (CopyOnWriteArrayList) ---");
        List<String> failSafeList = new CopyOnWriteArrayList<>(
            Arrays.asList("Alpha", "Beta", "Gamma", "Delta"));

        Iterator<String> fsIt = failSafeList.iterator();
        while (fsIt.hasNext()) {
            String s = fsIt.next();
            System.out.println("  Read: " + s);
            if (s.equals("Beta")) {
                failSafeList.add("Epsilon"); // modification allowed — no exception
            }
        }
        // 'Epsilon' was added but NOT seen by the iterator (snapshot)
        System.out.println("  List after iteration: " + failSafeList);
    }
}

Output

--- Fail-Fast (ArrayList) --- Read: Alpha Read: Beta ConcurrentModificationException caught! --- Fail-Safe (CopyOnWriteArrayList) --- Read: Alpha Read: Beta Read: Gamma Read: Delta List after iteration: [Alpha, Beta, Gamma, Delta, Epsilon]

Iterator Execution Flow — Flowchart

The flowchart below shows the complete lifecycle of an Iterator — from obtaining it, through traversal and safe removal, to exhaustion.

📦 collection.iterator()obtain Iterator object
❓ it.hasNext()?are more elements available?
false
✅ Iteration completeexit the while loop
📥 E e = it.next()retrieve + advance cursor
❓ modCount == expectedModCount?fail-fast check (every next())
mismatch
💥 ConcurrentModificationExceptioncollection modified externally
⚙️ Process element eread, compare, transform
❓ Remove element?conditional check
Yes
🗑️ it.remove()safe removal — updates modCount
🔁 Back to hasNext()continue loop

Code Execution Flow — from source to output

ConcurrentModificationException — Causes and All Fixes

ConcurrentModificationException is one of the most common runtime errors Java developers encounter. Understanding exactly when it occurs and all the ways to fix it is an essential production skill.

☕ JavaConcurrentModificationException — 4 Causes and Their Fixes
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

public class CMEFixDemo {
    public static void main(String[] args) {

        List<String> names = new ArrayList<>(
            Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve"));

        // ❌ CAUSE 1: remove() inside for-each loop
        // for (String n : names) { if (n.startsWith("C")) names.remove(n); }
        // → ConcurrentModificationException

        // ✅ FIX 1: Use Iterator.remove()
        List<String> fix1 = new ArrayList<>(names);
        Iterator<String> it = fix1.iterator();
        while (it.hasNext()) {
            if (it.next().startsWith("C")) it.remove();
        }
        System.out.println("Fix 1 (Iterator.remove): " + fix1);

        // ✅ FIX 2: removeIf (Java 8+) — cleanest
        List<String> fix2 = new ArrayList<>(names);
        fix2.removeIf(n -> n.startsWith("C"));
        System.out.println("Fix 2 (removeIf)       : " + fix2);

        // ✅ FIX 3: Collect to new list via Stream, then reassign
        List<String> fix3 = names.stream()
            .filter(n -> !n.startsWith("C"))
            .collect(Collectors.toList());
        System.out.println("Fix 3 (Stream.filter)  : " + fix3);

        // ✅ FIX 4: Collect indices/elements to remove, bulk remove after loop
        List<String> fix4 = new ArrayList<>(names);
        List<String> toRemove = new ArrayList<>();
        for (String n : fix4) {
            if (n.startsWith("C")) toRemove.add(n);
        }
        fix4.removeAll(toRemove);
        System.out.println("Fix 4 (removeAll)      : " + fix4);

        // ✅ FIX 5: CopyOnWriteArrayList — for concurrent / multi-threaded
        List<String> fix5 = new CopyOnWriteArrayList<>(names);
        for (String n : fix5) {
            if (n.startsWith("C")) fix5.remove(n);  // safe — no CME
        }
        System.out.println("Fix 5 (COWAL)          : " + fix5);
    }
}

Output

Fix 1 (Iterator.remove): [Alice, Bob, David, Eve] Fix 2 (removeIf) : [Alice, Bob, David, Eve] Fix 3 (Stream.filter) : [Alice, Bob, David, Eve] Fix 4 (removeAll) : [Alice, Bob, David, Eve] Fix 5 (COWAL) : [Alice, Bob, David, Eve]

Implementing Custom Iterator and Iterable

You can make any custom class support for-each loops by implementing Iterable<T> and returning a custom Iterator<T>. This is the Iterator Design Pattern — it decouples traversal logic from the data structure.

☕ JavaCustom Iterator — NumberRange Class
import java.util.Iterator;
import java.util.NoSuchElementException;

// Custom Iterable class — a range of integers [start, end]
class NumberRange implements Iterable<Integer> {

    private final int start;
    private final int end;
    private final int step;

    public NumberRange(int start, int end, int step) {
        this.start = start;
        this.end   = end;
        this.step  = step;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new RangeIterator();
    }

    // Private inner class — the actual Iterator implementation
    private class RangeIterator implements Iterator<Integer> {

        private int current = start;

        @Override
        public boolean hasNext() {
            return step > 0 ? current <= end : current >= end;
        }

        @Override
        public Integer next() {
            if (!hasNext()) {
                throw new NoSuchElementException(
                    "No more elements in range [" + start + ", " + end + "]");
            }
            int val = current;
            current += step;
            return val;
        }
    }
}

public class CustomIteratorDemo {
    public static void main(String[] args) {

        // for-each works because NumberRange implements Iterable
        System.out.print("Range 1-10 step 1  : ");
        for (int n : new NumberRange(1, 10, 1)) {
            System.out.print(n + " ");
        }

        System.out.print("\nRange 0-20 step 5  : ");
        for (int n : new NumberRange(0, 20, 5)) {
            System.out.print(n + " ");
        }

        System.out.print("\nRange 10-1 step -2 : ");
        for (int n : new NumberRange(10, 1, -2)) {
            System.out.print(n + " ");
        }

        // Multiple independent iterators from same object
        NumberRange evens = new NumberRange(2, 10, 2);
        Iterator<Integer> it1 = evens.iterator();
        Iterator<Integer> it2 = evens.iterator();
        System.out.println("\nit1.next(): " + it1.next());  // 2
        System.out.println("it2.next(): " + it2.next());  // 2 — independent!
        System.out.println("it1.next(): " + it1.next());  // 4
    }
}

Output

Range 1-10 step 1 : 1 2 3 4 5 6 7 8 9 10 Range 0-20 step 5 : 0 5 10 15 20 Range 10-1 step -2 : 10 8 6 4 2 it1.next(): 2 it2.next(): 2 it1.next(): 4

Best Practices for Using Iterator

Following these best practices will prevent the most common Iterator bugs and keep your traversal code clean, correct, and maintainable:

  • ✅ 1. Always Use Iterator.remove() — Never Direct Collection.remove() Inside a Loop — Calling list.remove(element) or map.remove(key) directly inside a for-each or Iterator while loop causes ConcurrentModificationException. Always use it.remove() for safe deletion during traversal. For bulk conditional removal, prefer list.removeIf(predicate) which is cleaner and internally uses an Iterator.

  • ✅ 2. Call next() Before remove() — iterator.remove() removes the last element returned by next(). Calling remove() before any next() call, or calling it twice consecutively without an intervening next(), throws IllegalStateException. Always follow the pattern: if (it.hasNext()) { E e = it.next(); if (condition) it.remove(); }.

  • ✅ 3. Check hasNext() Before Every next() Call — Calling next() on an exhausted iterator (hasNext() == false) throws NoSuchElementException. Always guard every next() call with a preceding hasNext() check — this is why the while (it.hasNext()) pattern is the canonical Iterator usage.

  • ✅ 4. Use for-each When You Do Not Need to Remove — For simple read-only traversal, for-each is cleaner, less error-prone, and equally performant. Reserve explicit Iterator usage for scenarios requiring removal, partial traversal, or concurrent access patterns.

  • ✅ 5. Use ListIterator for In-Place Modification of Lists — When you need to replace elements during traversal (e.g., normalize all strings to uppercase), ListIterator.set(E e) is the correct tool. Avoid index-based set() inside a for-each loop — it works but is error-prone and obscures intent.

  • ✅ 6. Use removeIf() as the Modern Alternative — For Java 8+, list.removeIf(predicate) is the cleanest way to conditionally remove elements without explicit Iterator handling. It is internally implemented with an Iterator, but gives you a declarative, readable one-liner.

  • ✅ 7. Implement Both Iterable and Iterator Correctly in Custom Classes — When implementing a custom Iterable, ensure that each call to iterator() returns a fresh, independent Iterator object with its own state. Never return the same Iterator object — that would mean only one traversal can be active at a time.

  • ❌ 8. Do Not Share Iterator Objects Between Threads — Iterator objects are not thread-safe. Even for thread-safe collections like ConcurrentHashMap, the iterator itself must not be shared across threads. Each thread should obtain its own iterator from the collection.

Real-World Code Examples

Example 1 — Shopping Cart Filter with Iterator

☕ JavaShopping Cart — Remove Unavailable Items
import java.util.*;

class CartItem {
    String  name;
    double  price;
    boolean inStock;

    CartItem(String name, double price, boolean inStock) {
        this.name    = name;
        this.price   = price;
        this.inStock = inStock;
    }

    @Override public String toString() {
        return name + "(₹" + price + ", " + (inStock ? "✅" : "❌") + ")";
    }
}

public class CartFilter {
    public static void main(String[] args) {

        List<CartItem> cart = new ArrayList<>(Arrays.asList(
            new CartItem("Laptop",      75000, true),
            new CartItem("Headphones",   2500, true),
            new CartItem("USB Hub",       850, false),
            new CartItem("Webcam",       3200, true),
            new CartItem("Mouse Pad",    450, false),
            new CartItem("Keyboard",    1800, true)
        ));

        System.out.println("Before filter: " + cart);

        // Remove out-of-stock items using Iterator
        Iterator<CartItem> it = cart.iterator();
        while (it.hasNext()) {
            if (!it.next().inStock) it.remove();
        }

        System.out.println("\nIn-stock items:");
        cart.forEach(item -> System.out.printf("  %-15s ₹%.0f%n", item.name, item.price));

        // Calculate total using Iterator
        double total = 0;
        Iterator<CartItem> totalIt = cart.iterator();
        while (totalIt.hasNext()) total += totalIt.next().price;
        System.out.printf("%nCart Total: ₹%.0f%n", total);
    }
}

Output

Before filter: [Laptop(₹75000.0, ✅), Headphones(₹2500.0, ✅), USB Hub(₹850.0, ❌), Webcam(₹3200.0, ✅), Mouse Pad(₹450.0, ❌), Keyboard(₹1800.0, ✅)] In-stock items: Laptop ₹75000 Headphones ₹2500 Webcam ₹3200 Keyboard ₹1800 Cart Total: ₹84500

Example 2 — Text Processor with ListIterator

☕ JavaText Normalizer with ListIterator
import java.util.*;

public class TextNormalizer {
    public static void main(String[] args) {

        List<String> tokens = new ArrayList<>(Arrays.asList(
            "  Hello  ", "WORLD", "", "java", "  Iterator  ",
            "null", "PATTERN", " ", "design"
        ));

        System.out.println("Raw tokens: " + tokens);

        ListIterator<String> lit = tokens.listIterator();
        while (lit.hasNext()) {
            String token = lit.next();
            String trimmed = token.trim();

            if (trimmed.isEmpty() || trimmed.equals("null")) {
                lit.remove();               // remove blank/null tokens
            } else {
                // Normalize: trim + title case
                String normalized = trimmed.substring(0, 1).toUpperCase()
                    + trimmed.substring(1).toLowerCase();
                lit.set(normalized);        // replace in place
            }
        }

        System.out.println("Normalized : " + tokens);

        // Reverse the cleaned list using ListIterator
        System.out.print("Reversed   : ");
        ListIterator<String> rev = tokens.listIterator(tokens.size());
        while (rev.hasPrevious()) {
            System.out.print(rev.previous() + " ");
        }
    }
}

Output

Raw tokens: [ Hello , WORLD, , java, Iterator , null, PATTERN, , design] Normalized : [Hello, World, Java, Iterator, Pattern, Design] Reversed : Design Pattern Iterator Java World Hello

Practice This Code — Live Editor

Advantages and Disadvantages of Iterator

Iterator is a well-designed interface that solves specific traversal problems cleanly — but it comes with its own set of constraints. Knowing both sides makes you a more effective Java developer.

✅ Advantages
Safe Element Removal During TraversalIterator.remove() is the only safe way to remove elements while iterating. It updates the collection's internal modCount to stay in sync, preventing ConcurrentModificationException that direct removal would cause.
Universal — Works on Any CollectionThe same Iterator-based traversal code works on ArrayList, LinkedList, HashSet, TreeSet, PriorityQueue, or any custom Iterable. This is the Iterator Design Pattern — decoupling traversal from data structure.
Lazy Evaluation — Elements Fetched On DemandIterator does not pre-load all elements. Each next() call fetches one element from the source. This is memory-efficient for large collections and enables early termination (break) without processing all elements.
Powers for-each and Stream APIJava's for-each loop is syntactic sugar over Iterator. The Stream API's spliterator() is built on the same traversal concept. Understanding Iterator unlocks a deeper understanding of how both features work.
Multiple Independent CursorsA collection can produce multiple independent iterators simultaneously, each with its own position. Two threads can traverse the same list independently — as long as neither modifies it (for fail-fast collections).
Extensible with ListIteratorFor lists, ListIterator extends Iterator to add bidirectional traversal, in-place replacement, and insertion — a full read-write bidirectional cursor that no other iteration mechanism provides.
❌ Disadvantages
Forward-Only (for basic Iterator)The base Iterator only moves forward. Once next() is called, you cannot go back. For backward traversal, you need ListIterator (lists only) or obtain a fresh iterator each time.
Cannot Add Elements (base Iterator)Iterator.remove() is the only mutation it supports — no add(). To add elements during traversal, use ListIterator (lists only) or collect additions in a separate list and addAll() after the loop.
Verbose Compared to for-eachFor simple read-only traversal, explicit Iterator usage is more verbose than for-each. Three lines (declare iterator, while loop, next()) replace one clean for-each line. Prefer for-each unless removal is needed.
Fail-Fast Behaviour Can Be SurprisingBeginners are often confused when ConcurrentModificationException appears even in single-threaded code — caused simply by removing from a list inside a for-each. The fail-fast behaviour is correct and protective, but its cause is not immediately obvious.
No Index AccessIterator has no concept of index. You cannot ask 'what position am I at?' with a basic Iterator. For index-aware traversal, use ListIterator.nextIndex() / previousIndex() or a traditional for loop.

Java Iterator — Interview Questions

These are the most frequently asked interview questions on the Java Iterator interface for Java developer and backend engineer positions.

Practice Questions — Test Your Knowledge

Test your understanding of Java Iterator with these practice questions. Attempt each before revealing the answer.

1. What is the output? List<String> list = new ArrayList<>(Arrays.asList("A","B","C","D")); Iterator<String> it = list.iterator(); it.next(); it.next(); it.remove(); System.out.println(list);

Easy

2. Why does this code throw ConcurrentModificationException and what is the fix? for (String s : list) { if (s.equals("B")) list.remove(s); }

Easy

3. What is the output? List<Integer> nums = new ArrayList<>(Arrays.asList(1,2,3,4,5)); ListIterator<Integer> lit = nums.listIterator(nums.size()); while (lit.hasPrevious()) { System.out.print(lit.previous() + " "); }

Easy

4. What exception does it.next() throw when the iterator is exhausted, and why?

Easy

5. You have a List<String> and want to normalize each element (trim + capitalize). Which iteration method is correct: Iterator, ListIterator, or for-each? Why?

Medium

6. What is the difference between how CopyOnWriteArrayList and ArrayList handle concurrent modification during iteration?

Medium

7. What happens when you call it.remove() before calling it.next()? Iterator<String> it = list.iterator(); it.remove(); // called without prior next()

Medium

8. Implement a class EvenNumbers that is Iterable<Integer> and produces all even numbers from 2 to N inclusive. Show its usage with for-each.

Hard

Conclusion — Mastering Java Iterator

The Iterator interface is deceptively simple — just three methods — yet it underpins every collection traversal in Java. It powers the for-each loop, defines the boundary between the Iterator Design Pattern's traversal logic and data structure, and solves the critical problem of safe element removal during iteration that trips up beginners and produces production bugs.

Mastering Iterator means understanding when to use it over for-each (when removal or partial traversal is needed), knowing its relationship with ListIterator (for in-place modification and backward traversal), understanding the fail-fast mechanism behind ConcurrentModificationException (and all five ways to fix it), and being able to implement custom Iterable classes for domain objects.

ScenarioBest Choice
Simple read-only traversal — single-threaded✅ for-each loop — cleanest syntax
Remove elements during traversal✅ Iterator with it.remove()
Conditional bulk removal — Java 8+✅ list.removeIf(predicate) — cleanest
Replace elements in place during traversal✅ ListIterator with lit.set(newValue)
Backward traversal of a List✅ ListIterator starting from list.size()
Insert elements during traversal✅ ListIterator with lit.add(element)
Safe iteration in multi-threaded environment✅ CopyOnWriteArrayList / ConcurrentHashMap (fail-safe)
Make a custom class support for-each✅ Implement Iterable<T> + return custom Iterator<T>

Your next steps: explore Spliterator (Java 8) — the parallel-capable successor to Iterator used by the Stream API; study how Java 21's virtual threads interact with fail-safe iterators in concurrent workloads; and understand how Stream.iterate() and Stream.generate() provide infinite lazy sequences built on the same cursor concept as Iterator.

Remember: for-each for reading, Iterator.remove() for safe deletion, ListIterator.set() for replacement, removeIf() for clean bulk deletion, and always guard next() with hasNext(). These five habits will keep your collection traversal code correct, expressive, and production-ready. ☕

Frequently Asked Questions (FAQ)