Iterator and ListIterator in Java - Quiz

Total: 6 questions

1. 

What is an Iterator in Java and what methods does it have?

Iterator<E> (package java.util, Java 1.2) is a cursor interface for visiting the elements of a collection one by one without exposing how the collection stores them: the same loop works for an ArrayList, a HashSet or an ArrayDeque. You get one from the iterator() method of Iterable, which every collection has.

The cursor never sits on an element; it sits between elements. A fresh iterator is positioned before the first element, and each next() steps over one element and returns it. A plain Iterator cannot move back or be reset — for another pass you ask for a new iterator.

  • boolean hasNext() — whether there is another element; does not move the cursor.
  • E next() — returns the next element; if there are no more elements — NoSuchElementException.
  • default void remove() — removes the element returned by the last next().
  • default void forEachRemaining(Consumer) (Java 8) — runs an action for every element not yet visited.

The standard loop is while (it.hasNext()) with exactly one call to it.next() inside: two next() calls per iteration take two elements and throw NoSuchElementException when the number of elements is odd.

2. 

How do you remove elements from a collection while iterating over it, and which exceptions can Iterator.remove() throw?

The only safe way to remove elements during the iteration is the iterator’s own remove(): it deletes the element returned by the last next() and keeps the iterator in sync with the collection. Calling list.remove() in the same loop leads to ConcurrentModificationException.

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    if (it.next() % 2 == 0) {
        it.remove();
    }
}
System.out.println(numbers); // [1, 3]
  • IllegalStateException — if remove() is called before the first next() or twice in a row: at most one remove() is allowed after each next().
  • UnsupportedOperationException — on unmodifiable collections (for example, List.of(...)) and on iterators that do not override the default remove().

To remove by a condition without an explicit loop there is removeIf() (Java 8); an explicit iterator is needed when removal comes with extra work — counting, logging, or moving the element elsewhere.

3. 

Why does ConcurrentModificationException happen, and can you rely on it?

ConcurrentModificationException is thrown by an iterator’s next() when, after the iterator was created, the collection was structurally modified by anything other than that iterator: add(), remove() or clear() on the collection itself. It happens in single-threaded code too: “concurrent” means “at the same time as the iteration”, not “from another thread”.

How it works: ArrayList, HashMap and the other java.util collections keep a counter of structural modifications, modCount. The iterator copies it on creation (expectedModCount) and compares the two in every next(); if they differ, it fails immediately. Such iterators are called fail-fast. Iterator.remove() updates both counters, which is why removing through it is safe. A for-each loop uses the same iterator and follows the same rule.

You must not rely on the exception: fail-fast works on a best-effort basis. If you remove the second-to-last element of an ArrayList inside for-each, hasNext() returns false before next() can check anything — the loop silently ends and skips the last element. Modify a collection during iteration only through Iterator.remove(), ListIterator.set()/add() or removeIf(); if you need to add elements that should not be visited, iterate over a copy of the collection.

4. 

What is the difference between Iterator and Iterable, and how is the for-each loop related to them?

Iterable<T> (java.lang, Java 5) is what you iterate over: it has one abstract method, iterator(), and the Java 8 default methods forEach() and spliterator(). Iterator<T> (java.util) is how you iterate: a single-use cursor with state. One Iterable can hand out any number of independent iterators. Collection extends Iterable, which is why for-each works with every list, set and queue.

For an Iterable, the compiler turns for-each into an ordinary iterator loop:

for (String s : list) { ... }
// becomes
for (Iterator<String> i = list.iterator(); i.hasNext(); ) {
    String s = i.next();
    ...
}

The iterator variable is hidden, so you cannot call remove() inside for-each. For arrays, for-each compiles into an index loop. The loop accepts nothing else: for (String key : map) does not compile because Map does not implement Iterable (iterate over keySet(), values() or entrySet() instead), and you cannot pass an Iterator itself to for-each either — the error is for-each not applicable to expression type.

5. 

How do you write a custom Iterator and Iterable so that objects of your class work in a for-each loop?

The class implements Iterable<T>, and its iterator() method returns a new Iterator<T> object with hasNext() and next() on every call:

public class Countdown implements Iterable<Integer> {
    private final int start;

    public Countdown(int start) {
        this.start = start;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            private int current = start;

            @Override
            public boolean hasNext() {
                return current > 0;
            }

            @Override
            public Integer next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                return current--;
            }
        };
    }
}
// for (int n : new Countdown(3)) prints 3 2 1
  • A new iterator for every iterator() call lets you iterate the object any number of times; if you return the same iterator stored in a field, a second for-each prints nothing.
  • By contract, next() throws NoSuchElementException when the elements run out; it does not return null.
  • Implementing remove() is optional: the default version throws UnsupportedOperationException. forEach() comes for free as a default method of Iterable.
  • An anonymous class with the diamond, new Iterator<>() { ... }, compiles only since Java 9; on Java 8 you specify the type explicitly.
6. 

How does ListIterator differ from Iterator, and how do you iterate a list from the end?

ListIterator<E> extends Iterator and exists only for List (Set and Queue do not have it). It adds six methods of its own to those of Iterator: hasPrevious() and previous() for moving backwards, nextIndex() and previousIndex() for the cursor position, set(e) for replacing and add(e) for inserting an element.

You get it with listIterator() (cursor before the first element) or listIterator(int index). Calling listIterator(list.size()) puts the cursor at the end, which is how you start iterating from the back:

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
ListIterator<String> it = list.listIterator(list.size());
while (it.hasPrevious()) {
    System.out.print(it.previousIndex() + ":" + it.previous() + " ");
}
// 2:C 1:B 0:A

Rules for changes: set() and remove() act on the element returned by the last next() or previous(); after add() or remove() they throw IllegalStateException, so in one step call set() first and add() second. add() inserts the element before the cursor, and the following next() does not return it — inserting on every step never makes the loop run forever. On a fixed-size list from Arrays.asList(), set() works, while add() and remove() throw UnsupportedOperationException.

Page 1 of 1