๐ŸŽ“ A1Learner
๐Ÿ”
0 of 40 lessons completed

Java Iterating in Depth

A ListIterator is a cursor that walks a list in both directions. It can also change the list safely while the walk is running.

Both directions

The iterator walks to the end of the list. It then walks back to the start.

import java.util.ArrayList;
import java.util.ListIterator;
public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> numbers = new ArrayList<>();
    numbers.add(10);
    numbers.add(20);
    numbers.add(30);
    ListIterator<Integer> it = numbers.listIterator();
    while (it.hasNext()) {
      System.out.println(it.next());
    }
    while (it.hasPrevious()) {
      System.out.println(it.previous());
    }
  }
}

set rewrites the value just passed

Each value is read and then doubled in place through the iterator. The printed list shows the new values.

import java.util.ArrayList;
import java.util.ListIterator;
public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> numbers = new ArrayList<>();
    numbers.add(10);
    numbers.add(20);
    numbers.add(30);
    ListIterator<Integer> it = numbers.listIterator();
    while (it.hasNext()) {
      Integer value = it.next();
      it.set(value * 2);
    }
    System.out.println(numbers);
  }
}

Safe removal through the iterator

The iterator removes one name as it passes it. The list afterwards holds the surviving names in order.

import java.util.ArrayList;
import java.util.Iterator;
public class Main {
  public static void main(String[] args) {
    ArrayList<String> names = new ArrayList<>();
    names.add("Ana");
    names.add("Ben");
    names.add("Cid");
    names.add("Dee");
    Iterator<String> it = names.iterator();
    while (it.hasNext()) {
      String name = it.next();
      if (name.equals("Ben")) {
        it.remove();
      }
    }
    System.out.println(names);
  }
}

Adding during a for-each fails fast

The loop prints one name and then adds a name directly to the list. The next step of the walk detects the change and throws.

import java.util.ArrayList;
public class Main {
  public static void main(String[] args) {
    ArrayList<String> names = new ArrayList<>();
    names.add("Ana");
    names.add("Ben");
    names.add("Cid");
    for (String name : names) {
      System.out.println(name);
      names.add("Dee");
    }
  }
}

Example

import java.util.ArrayList;
import java.util.ListIterator;
public class Main {
  public static void main(String[] args) {
    ArrayList<String> names = new ArrayList<>();
    names.add("Ana");
    names.add("Ben");
    names.add("Cid");
    ListIterator<String> it = names.listIterator();
    while (it.hasNext()) {
      String name = it.next();
      it.set(name.toUpperCase());
    }
    System.out.println(names);
    while (it.hasPrevious()) {
      System.out.println(it.previous());
    }
  }
}

Key points

  • A ListIterator walks forward with next and backward with previous.
  • set replaces the element the iterator most recently passed.
  • remove through the iterator deletes the last element it passed, and the walk stays valid.
  • Iterators are fail-fast, meaning a walk stops with ConcurrentModificationException when the list changes behind the iterator.
  • Adding to a list inside its own for-each loop is an outside change, so the next step of the walk throws.

In this example, set replaces each name with its uppercase form while the iterator walks forward. The same iterator then walks backward and prints the names in reverse order.

Note

Replacing a value through set is not a structural change, so the walk continues.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What happens when a program adds an element to a list inside a for-each loop over that same list?