Topic 94 of 119 10. Java Collections Framework Interactive Tutorial

Java Iterator and ListIterator: Safe Collection Traversal and Modification

9 mins read Last updated: 22 Aug 2026 31,262 views

Direct Answer & Core Concept

Java collections such as ArrayList, LinkedList, HashSet, and LinkedHashSet store multiple elements, but sometimes you need to process those elements one by one.

Java provides the Iterator interface for traversing collections safely and consistently. For lists, Java also provides ListIterator, which supports forward and backward traversal and allows additional list modifications during iteration.

Understanding these interfaces is important when you need to remove or modify collection elements while iterating.

Detailed Explanation & Mechanics

What Is an Iterator in Java?

An Iterator is an object used to traverse elements of a collection one at a time.

It is available through the java.util package:

Main.java
Run Online
import java.util.Iterator;

A basic example:

Main.java
Run Online
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Amit");
        names.add("Priya");
        names.add("Rahul");
        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            System.out.println(name);
        }
    }
}

Output:

The iterator moves through the collection one element at a time.


Why Use Iterator?

You could use an enhanced for loop:

Main.java
Run Online
for (String name : names) {
    System.out.println(name);
}

But Iterator becomes particularly useful when you need to remove elements safely during traversal.

For example:

Main.java
Run Online
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("Rahul")) {
        iterator.remove();
    }
}

The iterator's remove() method removes the last element returned by next().


Iterator Interface

The main methods of Iterator are:

MethodPurpose
hasNext()Checks whether another element exists
next()Returns the next element
remove()Removes the last element returned by next()
forEachRemaining()Performs an action on remaining elements

The most frequently used methods are:


hasNext()

hasNext() checks whether another element is available.

Example:

Main.java
Run Online
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

It returns:

when another element is available and:

when traversal has reached the end.


next()

The next() method returns the next element.

Main.java
Run Online
Iterator<String> iterator = names.iterator();
String first = iterator.next();
System.out.println(first);

If no element remains and next() is called, the iterator can throw:

Therefore, the normal pattern is:

Main.java
Run Online
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

remove()

Iterator.remove() removes the last element returned by next().

Example:

Main.java
Run Online
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    int number = iterator.next();
    if (number > 20) {
        iterator.remove();
    }
}System.out.println(numbers);

Output:

This is a safe way to remove elements during iterator traversal.


Why Not Remove Directly from the Collection?

Consider this example:

Main.java
Run Online
for (Integer number : numbers) {
    if (number > 20) {
        numbers.remove(number);
    }
}

Modifying many collections structurally while using an iterator-backed loop can cause:

Main.java
Run Online
ConcurrentModificationException

Instead, use the iterator's own remove() method:

Main.java
Run Online
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    Integer number = iterator.next();
    if (number > 20) {
        iterator.remove();
    }
}

This tells the iterator that the removal is intentional and keeps its traversal state synchronized with the collection.


Iterator and ConcurrentModificationException

Many standard Java collections use a fail-fast iterator.

For example:

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("Amit");
names.add("Priya");
names.add("Rahul");
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("Priya")) {
        names.remove(name);
    }
}

This can result in:

Main.java
Run Online
ConcurrentModificationException

The problem is that the collection was structurally modified directly instead of through the iterator.

Use:

when removal is required during iterator traversal.

Fail-fast behavior is a best-effort safety mechanism, not a thread-safety guarantee.


Iterator with ArrayList

Iterator works with ArrayList.

Main.java
Run Online
List<String> languages = new ArrayList<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");
Iterator<String> iterator = languages.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Output:


Iterator with LinkedList

Iterator also works with LinkedList.

Main.java
Run Online
List<String> languages = new LinkedList<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");
Iterator<String> iterator = languages.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

The same iterator interface can be used even though the underlying collection implementation is different.


Iterator with HashSet

Iterator is not limited to lists.

You can iterate through a HashSet:

Main.java
Run Online
Set<String> languages = new HashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");
Iterator<String> iterator = languages.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Remember that HashSet does not guarantee a predictable iteration order.

Therefore, do not expect the elements to appear in insertion order.


Iterator with LinkedHashSet

LinkedHashSet also provides an iterator.

Main.java
Run Online
Set<String> languages = new LinkedHashSet<>();
languages.add("Java");
languages.add("Python");
languages.add("C++");
Iterator<String> iterator = languages.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Because LinkedHashSet maintains insertion order, its iterator normally traverses the elements in that order.


Iterator vs Enhanced for Loop

Both can be used for traversal.

Enhanced for loop

Main.java
Run Online
for (String name : names) {
    System.out.println(name);
}

Iterator

Main.java
Run Online
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

The enhanced for loop is usually simpler for basic reading.

Iterator is particularly useful when you need explicit iterator operations such as:


What Is ListIterator?

ListIterator is a specialized iterator for lists.

It extends the capabilities of Iterator and supports:

  • forward traversal

  • backward traversal

  • adding elements

  • replacing elements

  • removing elements

  • checking previous elements

  • obtaining indexes

It is available through:

Main.java
Run Online
import java.util.ListIterator;

ListIterator works with List implementations such as:


Basic ListIterator Example

Main.java
Run Online
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Amit");
        names.add("Priya");
        names.add("Rahul");
        ListIterator<String> iterator = names.listIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Output:


ListIterator Can Move Backward

This is one of the biggest differences between Iterator and ListIterator.

After moving forward, you can move backward using:

Example:

Main.java
Run Online
ListIterator<String> iterator = names.listIterator();
while (iterator.hasNext()) {
    iterator.next();
}while (iterator.hasPrevious()) {
    System.out.println(iterator.previous());
}

Output:


ListIterator Methods

Important ListIterator methods include:

MethodPurpose
hasNext()Checks for a next element
next()Returns the next element
hasPrevious()Checks for a previous element
previous()Returns the previous element
nextIndex()Returns the index of the next element
previousIndex()Returns the index of the previous element
remove()Removes the last returned element
set()Replaces the last returned element
add()Inserts an element at the iterator's current position

hasPrevious()

hasPrevious() checks whether an element exists before the current iterator position.

Example:

Main.java
Run Online
ListIterator<String> iterator = names.listIterator();
while (iterator.hasNext()) {
    iterator.next();
}while (iterator.hasPrevious()) {
    System.out.println(iterator.previous());
}

The second loop travels from the end toward the beginning.


previous()

previous() returns the previous element and moves the iterator backward.

Example:

Main.java
Run Online
ListIterator<String> iterator = names.listIterator();
iterator.next();
iterator.next();
System.out.println(iterator.previous());

The iterator moves back one position and returns the element before its current cursor position.


ListIterator Index Methods

ListIterator provides:

Main.java
Run Online
nextIndex()previousIndex()

Example:

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("Amit");
names.add("Priya");
names.add("Rahul");
ListIterator<String> iterator = names.listIterator();
System.out.println(iterator.nextIndex());
System.out.println(iterator.previousIndex());

Initially, the cursor is before the first element.

The output is:

After calling:

the cursor moves forward.


Using set() with ListIterator

ListIterator can replace the last element returned by next() or previous() using set().

Example:

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("Amit");
names.add("Priya");
names.add("Rahul");
ListIterator<String> iterator = names.listIterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("Priya")) {
        iterator.set("Priya Sharma");
    }
}System.out.println(names);

Output:

Main.java
Run Online
[Amit, Priya Sharma, Rahul]

The existing element is replaced.


Using add() with ListIterator

ListIterator can also insert an element.

Example:

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("Amit");
names.add("Rahul");
ListIterator<String> iterator = names.listIterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("Amit")) {
        iterator.add("Priya");
    }
}System.out.println(names);

Output:

The new element is inserted at the iterator's current position.


Using remove() with ListIterator

You can also remove the last element returned by next() or previous().

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("Amit");
names.add("Priya");
names.add("Rahul");
ListIterator<String> iterator = names.listIterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("Priya")) {
        iterator.remove();
    }
}System.out.println(names);

Output:


Iterator vs ListIterator

The differences are important:

FeatureIteratorListIterator
Works with collections generallyYesNo, list only
Forward traversalYesYes
Backward traversalNoYes
remove()YesYes
set()NoYes
add()NoYes
nextIndex()NoYes
previousIndex()NoYes

A simple rule is:

Main.java
Run Online
Iterator→ General collection traversalListIterator→ Advanced List traversal and modification

Starting ListIterator at a Specific Position

You can create a ListIterator starting at a particular index.

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("Amit");
names.add("Priya");
names.add("Rahul");
names.add("Suman");
ListIterator<String> iterator = names.listIterator(2);
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Output:

The iterator starts before the element at index 2.


Traversing a List in Both Directions

One powerful feature of ListIterator is bidirectional traversal.

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("Amit");
names.add("Priya");
names.add("Rahul");
ListIterator<String> iterator = names.listIterator();
System.out.println("Forward:");
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}System.out.println("Backward:");
while (iterator.hasPrevious()) {
    System.out.println(iterator.previous());
}

Output:

Main.java
Run Online
Forward:AmitPriyaRahulBackward:RahulPriyaAmit

Safe Removal with Iterator

Suppose you want to remove all even numbers.

A direct removal approach can be problematic:

Main.java
Run Online
for (Integer number : numbers) {
    if (number % 2 == 0) {
        numbers.remove(number);
    }
}

Instead:

Main.java
Run Online
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    Integer number = iterator.next();
    if (number % 2 == 0) {
        iterator.remove();
    }
}

This is the traditional iterator-based pattern for safe removal during traversal.


Modern Alternative: removeIf()

For a simple filtering operation, modern Java often provides an even simpler solution:

Main.java
Run Online
numbers.removeIf(number -> number % 2 == 0);

This can be preferable when the task is simply:

Remove every element matching this condition.

Use Iterator when you specifically need iterator-based traversal or more control over the traversal process.


Modifying Values During ListIterator Traversal

Suppose you want to convert every name to uppercase.

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("amit");
names.add("priya");
names.add("rahul");
ListIterator<String> iterator = names.listIterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    iterator.set(name.toUpperCase());
}System.out.println(names);

Output:

set() replaces the last element returned by the iterator.


Iterator Cursor Concept

An iterator can be imagined as a cursor positioned between elements.

For:

the cursor starts conceptually here:

After:

it moves:

After another next():

This cursor model is especially important for understanding ListIterator.


Understanding ListIterator Cursor

Suppose the list is:

Initially:

After next():

After another next():

Calling previous() moves the cursor backward:

This is why ListIterator can move in both directions.


Important Iterator Rule for remove()

The remove() operation has a specific relationship with next() or previous().

You normally need to call:

or:

before calling:

Calling remove() repeatedly without another appropriate traversal operation can cause:

For example:

Main.java
Run Online
Iterator<String> iterator = names.iterator();
iterator.remove();

is invalid because the iterator has not yet returned an element.


Important ListIterator Rule for set()

set() also operates on the element most recently returned by:

or:

For example:

Main.java
Run Online
ListIterator<String> iterator = names.listIterator();
iterator.next();
iterator.set("New Name");

is valid.

But calling set() before returning an element can cause:

Certain iterator operations such as add() also affect when set() or remove() can next be used, so the iterator's modification rules should be followed carefully.


Iterator and UnsupportedOperationException

Not every collection supports modification through its iterator.

For example, an iterator obtained from an unmodifiable collection may not support:

In such cases, a modification attempt can throw:

Main.java
Run Online
UnsupportedOperationException

This is different from ConcurrentModificationException.

  • UnsupportedOperationException means the operation is not supported.

  • ConcurrentModificationException can indicate an unsupported structural modification was detected during fail-fast traversal.


Iterator with Map

A Map is not a Collection, so you do not directly call:

Instead, you obtain an iterator from one of its collection views.

For example:

Main.java
Run Online
Map<Integer, String> users = new HashMap<>();
users.put(101, "Amit");
users.put(102, "Priya");
Iterator<Map.Entry<Integer, String>> iterator = users.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<Integer, String> entry = iterator.next();
    System.out.println( entry.getKey() + " = " + entry.getValue());
}

This is the standard way to iterate through map entries using an iterator.


Removing Map Entries with Iterator

You can safely remove map entries through the iterator obtained from entrySet():

Main.java
Run Online
Iterator<Map.Entry<Integer, String>> iterator = users.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<Integer, String> entry = iterator.next();
    if (entry.getKey() == 101) {
        iterator.remove();
    }
}

The corresponding mapping is removed from the map.


Iterator vs Stream

Java Streams provide another way to process collections:

Main.java
Run Online
names.stream() .filter(name -> name.startsWith("A")) .forEach(System.out::println);

Streams are excellent for declarative data processing.

Iterators are useful when you need explicit traversal control, especially when performing iterator-supported modifications.

A simple distinction:

Main.java
Run Online
Iterator→ Explicit traversalStream→ Declarative processing pipeline

They solve related but different problems.


Iterator in Real-World Applications

Iterators can be useful when:

  • removing matching elements while traversing

  • processing collections incrementally

  • working with different collection implementations through a common API

  • traversing sets without relying on indexes

  • iterating over map entries

  • implementing collection-processing logic

  • needing explicit traversal state

ListIterator is particularly useful for:

  • editing lists while traversing

  • moving forward and backward

  • inserting elements

  • replacing elements

  • navigating by list position


Complete Iterator Example

Main.java
Run Online
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(10);
        numbers.add(15);
        numbers.add(20);
        numbers.add(25);
        numbers.add(30);
        Iterator<Integer> iterator = numbers.iterator();
        while (iterator.hasNext()) {
            Integer number = iterator.next();
            if (number % 2 == 0) {
                iterator.remove();
            }
        } System.out.println(numbers);
    }
}

Output:

The iterator safely removes the even numbers.


Complete ListIterator Example

Main.java
Run Online
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("amit");
        names.add("priya");
        names.add("rahul");
        ListIterator<String> iterator = names.listIterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            iterator.set(name.toUpperCase());
        } System.out.println("Forward:");
        System.out.println(names);
        System.out.println("Backward:");
        while (iterator.hasPrevious()) {
            System.out.println(iterator.previous());
        }
    }
}

Output:

Main.java
Run Online
Forward:[AMIT, PRIYA, RAHUL]Backward:RAHULPRIYAAMIT

This example demonstrates both modification and backward traversal.


Common Mistakes with Iterator

Mistake 1: Calling next() Without Checking hasNext()

Avoid:

Main.java
Run Online
while (true) {
    System.out.println(iterator.next());
}

This can eventually throw NoSuchElementException.

Prefer:

Main.java
Run Online
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Mistake 2: Removing Directly from the Collection

Avoid modifying the collection directly during iterator traversal:

Main.java
Run Online
for (String name : names) {
    names.remove(name);
}

Use:

Main.java
Run Online
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    String name = iterator.next();
    if (condition) {
        iterator.remove();
    }
}

Mistake 3: Expecting Iterator to Move Backward

A normal Iterator only supports forward traversal.

If backward traversal is required, use:

with a List.


Mistake 4: Using ListIterator with a Set

ListIterator is designed for List implementations.

You cannot obtain a ListIterator directly from a HashSet.

Use:

for sets.


Mistake 5: Assuming Iterator Makes Collections Thread-Safe

An iterator does not automatically make a collection safe for concurrent access.

Thread safety is a separate concern.


Iterator vs ListIterator: Easy Memory Trick

Remember:

Main.java
Run Online
Iterator↓ForwardRemoveListIterator↓ForwardBackwardRemoveReplaceAddIndexes

This makes the difference easy to remember during interviews and exams.


Frequently Asked Questions

What is Iterator in Java?

Iterator is an interface used to traverse elements of a collection sequentially.

What does hasNext() do?

It checks whether another element is available during forward traversal.

What does next() do?

It returns the next element and advances the iterator.

Can Iterator remove elements?

Yes, through Iterator.remove(), when the underlying collection and iterator support removal.

What is ListIterator?

ListIterator is an advanced iterator designed for lists. It supports forward and backward traversal and additional operations such as add() and set().

Can ListIterator move backward?

Yes. Use:

Can Iterator modify elements?

A normal Iterator provides remove() but does not provide set() or add(). ListIterator provides those additional list modification operations.

Does Iterator work with HashSet?

Yes.

Does ListIterator work with HashSet?

No. ListIterator is for lists.

What is ConcurrentModificationException?

It is commonly associated with fail-fast iterators detecting structural modification of a collection outside the iterator's supported modification mechanism while traversal is in progress.

Is Iterator thread-safe?

No. An iterator itself does not make a collection thread-safe.

Graphics and Design Ideas

Graphic 1: Iterator Forward Traversal

Show:

Main.java
Run Online
[A] →[B] →[C] →[D] ↑Iterator

Animate the iterator moving from left to right.

Graphic 2: ListIterator Bidirectional Traversal

Show:

Main.java
Run Online
Forward →[A] ↔[B] ↔[C] ↔[D] ← Backward

Use two arrows to visually explain the difference between Iterator and ListIterator.

Graphic 3: Safe Removal

Show:

Main.java
Run Online
Collection↓Iterator↓next()↓Check condition↓remove()↓Collection updated

This clearly demonstrates why iterator.remove() is safer than directly changing the collection during traversal.

Graphic 4: Iterator vs ListIterator

Create a side-by-side comparison:

Main.java
Run Online
Iterator ListIteratorForward ✓ Forward ✓Backward ✗ Backward ✓Remove ✓ Remove ✓Set ✗ Set ✓Add ✗ Add ✓Index ✗ Index ✓

This can work well as the main featured image for the tutorial.

Runnable Code Example: Java Iterator and ListIterator

Java 17+
Main.java
Run Online
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorDemo {
    public static void main(String[] args) {
        List<String> items = new ArrayList<>(List.of("Java", "Deprecated", "Kotlin", "Deprecated"));
        // Safe element removal using Iterator
        Iterator<String> it = items.iterator();
        while (it.hasNext()) {
            String current = it.next();
            if ("Deprecated".equals(current)) {
                it.remove();
                // Safe removal
            }
        } System.out.println("Cleaned List: " + items);
    }
}

Common Java Technical Interview Questions & Answers

Interview Prep
Q1

What causes a ConcurrentModificationException during iteration in Java?

Modifying a collection structurally (e.g. list.add() or list.remove()) while iterating over it with a for-each loop or Iterator without using iterator.remove().
Q2

What extra capabilities does ListIterator have over standard Iterator?

Bidirectional traversal (previous/hasNext), element replacement (set), element insertion (add), and index inspection.

Key Takeaways, Best Practices & Clean Code Rules

Java Best Practice & Production Rules

Use Collection.removeIf(predicate) in modern Java (8+) for clean and expressive conditional element removal.