Java Iterator and ListIterator with Examples

Java Iterator and ListIterator with Examples


In this beginner-friendly guide to Java Iterator and ListIterator with examples, you will learn how to traverse and modify Java collections safely and effectively. We will cover what is iterator in Java, what is listiterator in Java, how to iterate over list in Java, practical code samples, and common mistakes to avoid. If you are starting your Java journey, check our full Java series on CodDesire Java Tutorials.

Key takeaways: Java Iterator and ListIterator with examples

  • Iterator: works on any Collection, moves forward only, supports safe element removal with remove().
  • ListIterator: list-only, moves forward and backward, supports add(), set(), and remove() with call-order rules; index-aware with nextIndex()/previousIndex().
  • Avoid modifying the collection directly during iteration; use iterator methods to prevent ConcurrentModificationException.
  • For read-only passes, prefer for-each or streams; for in-place edits, use Iterator/ListIterator.

What is Iterator in Java?

Java Iterator forward traversal over a list, visualizing safe element removal during iteration
How a Java Iterator moves forward and removes items safely

An Iterator is an object that lets you loop through elements of a collection (like ArrayList, HashSet, etc.) in a forward-only direction. It provides a safe way to access elements one-by-one and, optionally, remove elements during iteration.

Why use Iterator?

  • To traverse any Collection type element-by-element.
  • To remove elements safely while iterating (without manual index management).
  • To avoid errors that happen when modifying a collection during a normal loop.

Core methods of Iterator

  • boolean hasNext() – Is there another element?
  • E next() – Get the next element.
  • void remove() – Remove the last returned element (optional; may not be supported).
  • default void forEachRemaining(Consumer) – Process the remaining elements (Java 8+).

Important rules:

  • remove() can be called at most once per successful next() call.
  • Some collections or views do not support remove and will throw UnsupportedOperationException.

Java Iterator example for beginners (iterate and print)

Code
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.Iterator;
class="cd-package">import java.util.List;

class="cd-keyword cd-access">public class IterateBasics {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");

        Iterator<String> it = names.iterator();

        while (it.hasNext()) {
            String name = it.next();
            System.out.println(name);
        }
    }
}
Output
Alice
Bob
Charlie

Iterator remove method example in Java

Here is how to remove blank names safely while iterating. This is a common “how to use iterator in java step by step” pattern:

Code
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.Iterator;
class="cd-package">import java.util.List;

class="cd-keyword cd-access">public class RemoveWhileIterating {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("");
        names.add("Bob");
        names.add("   ");
        names.add("Charlie");

        Iterator<String> it = names.iterator();
        while (it.hasNext()) {
            String n = it.next();
            if (n.isBlank()) {
                it.remove(); // safe: removes the blank string
            }
        }

        System.out.println(names);
    }
}
Output
[Alice, Bob, Charlie]

forEachRemaining example

Code
class="cd-package">import java.util.List;
class="cd-package">import java.util.Iterator;

class="cd-keyword cd-access">public class ForEachRemainingDemo {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<Integer> nums = List.of(1, 2, 3, 4, 5);
        Iterator<Integer> it = nums.iterator();

        // Skip first two
        it.next();
        it.next();

        // Print remaining using a method reference
        it.forEachRemaining(System.out::println);
    }
}
Output
3
4
5

What is ListIterator in Java?

Java ListIterator bidirectional traversal with insert and replace actions on a list
ListIterator lets you move both ways and modify the list at the cursor

ListIterator is a specialized iterator that works only with List implementations (like ArrayList, LinkedList). It supports bidirectional traversal (forward and backward) and allows index-aware updates. It is perfect when you need to traverse a list both ways or insert/update elements at the current cursor position.

Key features of ListIterator

  • Bidirectional: hasPrevious(), previous() in addition to hasNext(), next()
  • Index access: nextIndex(), previousIndex()
  • Modify in place: add(E), set(E), and remove() (with order rules)

Rules to remember:

  • set(E) is allowed only immediately after a successful next() or previous(), and not right after add or remove.
  • add(E) inserts the element at the cursor position; after add, a new next() or previous() is needed before calling set again.

Java ListIterator example to traverse list both ways

Code
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.List;
class="cd-package">import java.util.ListIterator;

class="cd-keyword cd-access">public class ListIteratorBothWays {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<String> cities = new ArrayList<>();
        cities.add("Delhi");
        cities.add("Mumbai");
        cities.add("Kolkata");

        ListIterator<String> li = cities.listIterator();

        System.out.println("Forward:");
        while (li.hasNext()) {
            int idx = li.nextIndex();
            String c = li.next();
            System.out.println(idx + " - " + c);
        }

        System.out.println("Backward and modify:");
        while (li.hasPrevious()) {
            int idx = li.previousIndex();
            String c = li.previous();
            if (c.startsWith("K")) {
                li.set(c.toUpperCase()); // replace last returned
            } else {
                li.add("City"); // insert a tag near current position
            }
            System.out.println(idx + " - " + c);
        }

        System.out.println("Final list: " + cities);
    }
}
Output
Forward:
0 - Delhi
1 - Mumbai
2 - Kolkata
Backward and modify:
2 - Kolkata
1 - Mumbai
0 - Delhi
Final list: [Delhi, City, Mumbai, City, KOLKATA]

Iterator vs ListIterator

Feature Iterator ListIterator
Works with Any Collection List only
Direction Forward only Forward and backward
Modify elements remove() (optional) add(), set(), remove() (with rules)
Index awareness No nextIndex(), previousIndex()
Use when You need simple forward traversal or safe removals You need bidirectional traversal or in-place insert/update with indices
Traversal flow (visual): Iterator vs ListIterator
Iterator (forward-only)
Start
→
iterator()
→
hasNext()
→
next()
→
process element
→
optional remove()
→
loop until hasNext() is false

ListIterator (bidirectional)
Start
→
listIterator() (front)
→
hasNext()/next()
→
set()/add()/remove()

Or start at end
→
listIterator(size) (end)
→
hasPrevious()/previous()
→
set()/add()/remove()

Rule reminders: call remove() or set() only after next()/previous(); after add(), call next() or previous() before using set() again.

How to iterate a Java ArrayList using Iterator

You can get an iterator from any list using list.iterator() and loop with hasNext() / next():

Code
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.Iterator;
class="cd-package">import java.util.List;

class="cd-keyword cd-access">public class IterateArrayList {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("Mango");
        fruits.add("Apple");
        fruits.add("Banana");

        Iterator<String> it = fruits.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

For-each loop, Iterator, and Streams: which to use?

  • The enhanced for-each loop internally uses an Iterator for collections. It’s great for read-only traversal.
  • If you must remove elements while iterating, prefer an explicit Iterator and call remove().
  • For read-only bulk operations and functional style, use streams; but streams are not designed for in-place edits.

Why does my Iterator throw ConcurrentModificationException?

Most non-concurrent collections (like ArrayList) provide “fail-fast” iterators that may throw ConcurrentModificationException if the collection is structurally modified from outside the iterator during iteration. This check is best-effort, not guaranteed.

Incorrect: removing inside for-each

Code
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.List;

class="cd-keyword cd-access">public class WrongRemoval {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<String> list = new ArrayList<>(List.of("a", "b", "c"));
        for (String s : list) {
            if (s.equals("b")) {
                list.remove(s); // may cause ConcurrentModificationException
            }
        }
        System.out.println(list);
    }
}
Output
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:...)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:...)
    ...

Correct: use Iterator.remove()

Code
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.Iterator;
class="cd-package">import java.util.List;

class="cd-keyword cd-access">public class CorrectRemoval {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<String> list = new ArrayList<>(List.of("a", "b", "c"));
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            if (it.next().equals("b")) {
                it.remove(); // safe
            }
        }
        System.out.println(list);
    }
}
Output
[a, c]

Unmodifiable lists and UnsupportedOperationException

Some lists cannot be modified (for example, List.of(...) or Collections.unmodifiableList(...)). Their iterators also do not allow removal or updates.

Code
class="cd-package">import java.util.Iterator;
class="cd-package">import java.util.List;

class="cd-keyword cd-access">public class UnmodifiableListDemo {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<String> fixed = List.of("A", "B");
        Iterator<String> it = fixed.iterator();
        it.next();
        it.remove(); // throws UnsupportedOperationException
    }
}

Concurrent collections tip

If iterations and modifications happen across threads, consider concurrent collections. For example, CopyOnWriteArrayList provides snapshot (weakly consistent) iterators that do not throw ConcurrentModificationException, but their iterator remove/add/set are unsupported.

Step-by-step: how to use Iterator in Java (simple checklist)

  1. Create or get your collection.
  2. Call iterator() to obtain an Iterator.
  3. Loop while hasNext() is true.
  4. Get the element with next().
  5. If needed, call remove() once per next() to delete the current element.
Code
class="cd-package">import java.util.Iterator;
class="cd-package">import java.util.LinkedList;
class="cd-package">import java.util.List;

class="cd-keyword cd-access">public class IteratorStepByStep {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<Integer> nums = new LinkedList<>(List.of(1, 0, 2, 0, 3));
        Iterator<Integer> it = nums.iterator();

        while (it.hasNext()) {
            Integer v = it.next();
            if (v == 0) {
                it.remove(); // remove zeros safely
            }
        }

        System.out.println(nums); // [1, 2, 3]
    }
}

Java ListIterator example to traverse and edit both ways

This example doubles even numbers and inserts a zero marker near odd numbers while iterating backwards using listIterator(size):

Code
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.List;
class="cd-package">import java.util.ListIterator;

class="cd-keyword cd-access">public class BidirectionalEdit {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<Integer> nums = new ArrayList<>(List.of(1, 2, 3, 4));

        ListIterator<Integer> li = nums.listIterator(nums.size()); // start at end
        while (li.hasPrevious()) {
            int v = li.previous();
            if (v % 2 == 0) {
                li.set(v * 2); // replace even with double
            } else {
                li.add(0);     // insert 0 at current cursor position
            }
        }

        System.out.println(nums);
    }
}
Output
[1, 0, 4, 3, 0, 8]

Common mistakes with Java Iterator for beginners

  • Calling remove() twice without calling next() in between: causes IllegalStateException.
  • Modifying the collection directly inside a for-each or while using an iterator: may cause ConcurrentModificationException.
  • Assuming every iterator supports remove(): unmodifiable lists and some views throw UnsupportedOperationException.
  • Using ListIterator on non-List collections: it only works for lists.
  • Forgetting to check hasNext() / hasPrevious() before calling next() / previous(): can cause NoSuchElementException.
  • Expecting fail-fast behavior to catch all concurrent modifications: it is best-effort and not guaranteed.

When to use Iterator vs ListIterator in Java

  • Use Iterator when you need forward-only traversal or to remove elements safely during a loop.
  • Use ListIterator when you need to traverse a list in both directions, insert elements at the current position, or replace elements in place with index context.
  • Use for-each or streams for simple, read-only traversal and clear code style.
  • If multiple threads modify a collection during iteration, consider concurrent collections such as CopyOnWriteArrayList or ConcurrentHashMap variants.
Quick practical checklist
  • Any Collection + need safe removals → use Iterator and call remove() after next().
  • Need backward traversal, insert, or replace during iteration → use ListIterator.
  • Just reading elements → use for-each or streams (no in-place edits).
  • Unmodifiable list (e.g., List.of(...)) → do not call remove()/add()/set().
  • Multi-threaded modifications → prefer concurrent collections; note CopyOnWriteArrayList iterators do not support modification methods.
  • Rule of thumb: remove()/set() only after a successful next()/previous(), at most once per returned element.
  • Start a backward pass from the end: list.listIterator(list.size()).

FAQ: Java Iterator and ListIterator with examples

What is the difference between Iterator and ListIterator in Java?

Iterator works on any collection and moves forward only with optional remove(). ListIterator works only on lists and supports forward and backward traversal, plus add(), set(), index methods, and remove() with specific call-order rules.

How do I iterate a Java ArrayList using Iterator?

Call Iterator<E> it = list.iterator() and loop with while (it.hasNext()) { E e = it.next(); }. See the “IterateArrayList” example above.

When should I use ListIterator instead of a for loop?

Use ListIterator when you need bidirectional traversal, need the current index, or want to insert or update elements during iteration. A simple for loop or for-each is fine for read-only passes.

Can I add or remove elements while iterating in Java?

Yes, but use the iterator’s own methods. With Iterator, use remove(). With ListIterator, you can use add(), set(), or remove() following the method-order rules. Avoid modifying the list directly while iterating with for-each.

Why does my Iterator throw ConcurrentModificationException?

Because the collection was structurally modified from outside the iterator during traversal. This fail-fast check is typical for non-concurrent collections and is best-effort. Switch to using iterator methods, or use a concurrent collection if modifications happen concurrently.

Sources / Further reading

Continue learning more Java concepts in our Java section on CodDesire.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted