Detailed explanation
Iterator and ListIterator are Java interfaces for traversing elements without exposing a collection’s internal representation. Iterator works with collections such as List, Set, and Queue through iterator(), and supports forward traversal using hasNext() and next(). ListIterator extends Iterator and is available specifically for lists through listIterator() or listIterator(int index). It adds backward traversal, position information, insertion, and replacement. This distinction matters when choosing between simply visiting elements and editing an ordered sequence during traversal.
An Iterator advances through elements using next(), which throws NoSuchElementException when no next element exists. Its remove() method removes the last element returned by next(), provided removal is supported. You must call next() before remove(), and you cannot remove twice after the same next() call. Although Iterator declares remove(), that operation is optional: an implementation may throw UnsupportedOperationException. Also, Map does not implement Collection; its keys, values, or entries can instead be traversed through their collection views.
A ListIterator has a cursor positioned between elements, rather than permanently pointing at one element. For a list of size n, there are n + 1 possible cursor positions. next() moves forward, while previous() moves backward; hasPrevious() checks whether backward traversal is possible. nextIndex() and previousIndex() report the indexes of elements those calls would return, using size() and -1 at the respective boundaries. listIterator(index) starts the cursor at that position. Calling next() followed immediately by previous() therefore returns the same element twice.
ListIterator.set(value) replaces the last element returned by next() or previous(); for example, after reading "x", set("y") replaces it in place. add(value) inserts immediately before the element that next() would return, and remove() deletes the last returned element. These modifications are optional and have state rules: after add() or remove(), set() requires another successful traversal call. Interviewers look for these details, not just method names. With fail-fast implementations such as ArrayList’s iterators, direct structural changes to the list during iteration can trigger ConcurrentModificationException; supported iterator methods provide the intended mutation path.
Key points to remember
- Iterator supports forward traversal; ListIterator extends it with bidirectional traversal.
- Iterator is available across Collection types; ListIterator is specific to List.
- Both declare remove(); ListIterator additionally declares add() and set(), with mutation support depending on the implementation.
- ListIterator exposes nextIndex() and previousIndex() and can start at a specified cursor position.
- Mutation methods have state restrictions: remove() needs an eligible traversal call, while add() can insert without one.
Common mistakes candidates make
- Assuming every iterator supports removal or every ListIterator supports insertion and replacement.
- Calling remove() before traversal, or calling set() immediately after add() or remove().
- Modifying an ArrayList directly during iteration instead of using supported iterator mutation methods.
Likely follow-up questions
What happens if next() is called immediately after ListIterator.add()?
It returns the element that would have been returned before the insertion, not the newly inserted element. An immediate previous() instead returns the newly inserted element.
Can a ListIterator modify a list created with Arrays.asList()?
Its set() operation is supported because Arrays.asList() returns a fixed-size list that allows replacement. Its add() and remove() operations throw UnsupportedOperationException because they change the list’s size.
Does ConcurrentModificationException guarantee that concurrent changes will be detected?
No; fail-fast detection is best-effort and is not a thread-safety guarantee. It can also occur in a single thread when code structurally modifies a collection directly while traversing it.
Interview tip: Start with scope and traversal direction, then explain set(), add(), and one cursor or mutation-state rule using a small list example.
