HuntExams Academy logo
JavaAdvanced#puzzle#collections

What is the output? List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); for (Integer i : list) { if (i == 1) list.remove(i); }

Answer

Modifying a List while iterating with a for-each loop (which uses an Iterator internally) throws ConcurrentModificationException, since ArrayList's iterator is fail-fast and detects the structural modification.

Example

List<Integer> list = new ArrayList<>(List.of(1, 2));
for (Integer i : list) {
  if (i == 1) list.remove(i); // throws ConcurrentModificationException
}

Detailed explanation

For this exact code, there is no printed output because it contains no printing statement. More importantly, with the standard OpenJDK ArrayList implementation, it completes without throwing ConcurrentModificationException, and the list ends as [2]. The supplied short answer therefore overgeneralises fail-fast behaviour. Removing an element directly during iterator-based traversal is unsafe, but it does not guarantee that an exception will actually be thrown in every case.

An enhanced for loop over a List uses an Iterator internally: it repeatedly calls hasNext() and then next(). ArrayList maintains a structural modification counter called modCount, and its iterator records an expectedModCount. Operations such as adding or removing elements change modCount. The iterator checks whether these counters match during next(); a mismatch causes ConcurrentModificationException. However, in this implementation, hasNext() compares the iterator's cursor with the current list size without checking those modification counters.

Here, the first next() returns Integer.valueOf(1) and advances the cursor to index 1. The comparison i == 1 unboxes i, so the condition is true. Because i has compile-time type Integer, list.remove(i) selects remove(Object), not remove(int): it removes the value 1 rather than the element at index 1. The remaining value 2 shifts to index 0, and the list size becomes 1. The iterator's cursor is also 1, so the following hasNext() returns false. No further next() call occurs, leaving the modification undetected and the remaining element unvisited.

This question tests exact execution tracing, overload resolution, and the limits of fail-fast detection. Fail-fast iterators detect programming errors on a best-effort basis; they are not a correctness or concurrency mechanism. Changing the initial list to [1, 2, 3] causes the next iteration to reach next() and detect the mismatch in this implementation. For safe removal, use an explicit iterator with iterator.remove() after next(), or use list.removeIf(x -> x == 1). Both express removal without bypassing the traversal's supported modification mechanism.

Key points to remember

  • No output is printed; the final list is [2] in the standard OpenJDK implementation.
  • An enhanced for loop over a List uses an Iterator internally.
  • Since i is Integer, list.remove(i) calls remove(Object), removing the value 1.
  • After removal, cursor and size both equal 1, so hasNext() ends traversal before another modification check.
  • Use Iterator.remove() or removeIf() for safe removal; never rely on fail-fast detection.

Common mistakes candidates make

  • Claiming that every structural modification during iteration immediately throws ConcurrentModificationException.
  • Treating list.remove(i) as index-based removal despite i being an Integer.
  • Assuming that the absence of an exception makes this iteration pattern safe or guarantees that every element was visited.

Likely follow-up questions

What changes if the list initially contains [1, 2, 3]?

After removing 1, the cursor is 1 and the size is 2, so hasNext() returns true. The subsequent next() detects the modification-counter mismatch and throws ConcurrentModificationException.

How can you safely remove matching elements using an iterator?

Use an explicit iterator: for (Iterator<Integer> it = list.iterator(); it.hasNext();) { if (it.next() == 1) it.remove(); }. Iterator.remove() updates the iterator's traversal state and expected modification count.

How do remove(1) and remove(Integer.valueOf(1)) differ?

remove(1) selects remove(int) and removes the element at index 1. remove(Integer.valueOf(1)) selects remove(Object) and removes the first occurrence of the value 1, if present.

Interview tip: State the exact result first, then trace the cursor and size to explain why the usual fail-fast exception does not occur here.

Related Interview Questions

1
JavaIntermediate#puzzle#oop

What is the output of this code involving static and instance initialization order? class A { static { System.out.println("static A"); } { System.out.println("instance A"); } A() { System.out.println("constructor A"); } } new A(); new A();

Open
2
JavaIntermediate#puzzle#fundamentals

What does this print? Integer a = 127; Integer b = 127; Integer c = 128; Integer d = 128; System.out.println(a == b); System.out.println(c == d);

Open
3
JavaBeginner#puzzle#operators

What is the output? public class Test { public static void main(String[] args) { System.out.println(10 / 3); System.out.println(10.0 / 3); System.out.println(10 % 3); } }

Open