Removing Elements During an ArrayList Traversal
A loop that skips
A warehouse list
units holds the number of units left of each product: 8, 3, 2, 12, and 4. Any product with fewer than 5 units goes on a reorder sheet and comes off this list.for (int i = 0; i < units.size(); i++)
{
if (units.get(i) < 5)
{
units.remove(i);
}
}
The pass with
i at 1 removes the 3. The 2 behind it shifts down to index 1, but i goes on to 2, so the 2 is never examined.| i | Examined | List after the pass |
|---|---|---|
| 0 | 8 | 8, 3, 2, 12, 4 |
| 1 | 3 | 8, 2, 12, 4 |
| 2 | 12 | 8, 2, 12, 4 |
| 3 | 4 | 8, 2, 12 |
The loop stops when
i reaches 4, since the size is 3, and the list ends as 8, 2, and 12. The loop throws no exception, because the condition calls size() before each pass and the size drops with each removal. So i never runs past the last index, though the list it leaves is wrong.Two ways to avoid skipping
After a removal at index
i, the next unexamined element is at index i, so the index shouldn't advance on that pass. A while loop can advance i only on passes that remove nothing.int i = 0;
while (i < units.size())
{
if (units.get(i) < 5)
{
units.remove(i);
}
else
{
i++;
}
}
This loop examines all five values and leaves 8 and 12.
The other way is to traverse from the last index down to 0. A removal shifts only the elements after it, which a backward traversal has examined already.
The enhanced for loop
Removing or adding inside an enhanced
for loop over the same list changes its size while the loop is running. That can throw a ConcurrentModificationException. Reading the elements in an enhanced loop is safe.