ArrayList Removal While Iterating
A student wants to remove all negative numbers from an ArrayList<Integer> nums and writes:
for (int i = 0; i < nums.size(); i++) {
if (nums.get(i) < 0) {
nums.remove(i);
}
}
With nums = [-1, -2, 5, -3, 4], the result is [-2, 5, -3, 4] - the first element is removed but the second negative survives. The student tries an enhanced for-loop instead and gets a ConcurrentModificationException. A teammate suggests iterating backward.
1. Explain the index bug in the first loop using the specific input, and why the enhanced for-loop throws `ConcurrentModificationException`.
Read the worked answer
When remove(i) executes, elements after i shift left by one, and size() decreases. The loop then increments i, skipping the element that moved into slot i.
Trace on [-1, -2, 5, -3, 4]:
i=0:-1 < 0, remove →[-2, 5, -3, 4].ibecomes1.i=1: nownums.get(1)is5, not-2.-2was skipped.i=2:-3 < 0, remove →[-2, 5, 4].ibecomes3, which issize()- loop ends.
Result [-2, 5, 4] region still contains -2. The bug: after a removal, the next element shifts into the current index, but the loop advances past it.
The enhanced for-loop uses an Iterator; ArrayList.remove structurally modifies the list, so the iterator's next next() detects the change and throws ConcurrentModificationException. You cannot remove via the list while iterating with the for-each.
2. Give two correct fixes and state the tradeoff between them.
Read the worked answer
Fix 1 - iterate backward:
for (int i = nums.size() - 1; i >= 0; i--) {
if (nums.get(i) < 0) {
nums.remove(i);
}
}
Removing index i shifts only elements above i, which have already been processed, so no element is skipped. Simple and safe.
Fix 2 - use an explicit Iterator:
Iterator<Integer> it = nums.iterator();
while (it.hasNext()) {
if (it.next() < 0) {
it.remove();
}
}
Iterator.remove() is the sanctioned way to modify during iteration and avoids the exception.
Tradeoff: backward indexing is easy to read and needs no import, but it is O(n) removals each O(n) in the worst case (shifts), so O(n²). The Iterator version is also O(n²) for ArrayList but expresses intent clearly and is required if you must iterate forward. A third option, nums.removeIf(x -> x < 0), is O(n) and clearest when available. The judgment call is readability versus control; for a beginner, backward iteration is the least error-prone.