Memra

List, ArrayList, LinkedList & iteration

List operations, ArrayList vs LinkedList trade-offs, Iterator/ListIterator, fail-fast iteration, and the defensive-copy fix for ConcurrentModificationException.

List: the ordered, indexed, duplicate-allowing collection

List<E> adds index-based operations to Collection: add(e), add(i, e), get(i), set(i, e), remove(i), indexOf(e), size(). Two main implementations make different performance trade-offs:

- ArrayList — backed by a resizable array. get(i)/set(i) are O(1); appending is amortised O(1); inserting or removing in the middle is O(n) (it shifts elements). This is the default choice. - LinkedList — a doubly-linked list. get(i) is O(n) (it walks the chain), but inserting/removing at a known position (especially the ends) is O(1). It also implements Deque, so it doubles as a queue/stack.

Rule of thumb: random access and iteration → ArrayList; heavy add/remove at the ends → LinkedList or ArrayDeque.

### Three ways to iterate

  1. for-each (for (E e : list)) — cleanest; use when you don't need the index and won't modify during the loop.
  2. Iteratorit.hasNext() / it.next(), with it.remove() to delete the last-returned element safely during iteration.
  3. ListIterator — bidirectional (hasPrevious/previous), can set and add during traversal.

### Fail-fast and ConcurrentModificationException

Collection iterators are fail-fast: if the underlying collection is *structurally modified* (an add or remove that changes its size) while you are iterating it through anything other than the iterator's own remove, the iterator detects the change and throws ConcurrentModificationException (CME) on the next next(). This is a safety feature — it surfaces a bug instead of silently corrupting the traversal.

for (Event e : eventList)
    if (e.isDone())
        eventList.remove(e);   // BUG: modifies the list mid-iteration → CME

Worked example — the Greenhouse fix. The project's Controller.run() needs to fire ready events and remove them from the list during the same loop. It avoids the CME by iterating a defensive copy while mutating the original:

public void run() {
    while (eventList.size() > 0)
        // iterate a fresh copy, mutate the real list:
        for (Event e : new ArrayList<Event>(eventList))
            if (e.ready()) {
                System.out.println(e);
                e.action();
                eventList.remove(e);   // safe: removing from eventList, not the copy
            }
}

The loop walks new ArrayList<Event>(eventList) — a snapshot that is never modified — while eventList.remove(e) shrinks the *original*. No iterator over eventList is active, so no CME. The other correct fixes for the simpler delete-while-looping case are Iterator.remove() or list.removeIf(Event::isDone).

NORMAL ~/memra/learn/comp-308/list-arraylist-linkedlist-iteration utf-8 LF