Why Adding or Removing Elements Inside a Java foreach Loop Fails
This article explains why modifying a collection during a Java foreach loop triggers a ConcurrentModificationException, explores the underlying fail‑fast mechanism in ArrayList, and demonstrates safe alternatives such as using an Iterator's remove method or a CopyOnWriteArrayList.
ArrayList
A classic interview question asks why you cannot add or remove elements inside a Java foreach loop. The article analyzes this restriction and demonstrates the resulting ConcurrentModificationException.
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// Populate list
for (int i = 0; i < 10; i++) {
list.add(i + "");
}
for (String s : list) {
if ("5".equals(s)) {
list.remove(5);
}
System.out.println(s);
}
}Running this code produces:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
at java.util.ArrayList$Itr.next(ArrayList.java:861)The reason is that the foreach construct is compiled to use an Iterator internally. When the underlying list is modified directly, the iterator detects a mismatch between modCount and expectedModCount and throws the exception.
In ArrayList, modCount tracks the number of structural modifications. The remove methods increment this counter:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
// shift elements, clear last slot, adjust size
return oldValue;
}
public boolean remove(Object o) {
// ...
fastRemove(index);
return true;
}
private void fastRemove(int index) {
modCount++;
// shift elements, clear last slot
}The iterator stores the value of modCount at creation as expectedModCount: final int expectedModCount = modCount; If modCount changes without the iterator's knowledge, checkForComodification throws the exception, implementing the fail‑fast mechanism.
To avoid the exception, use the iterator's own remove method:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer integer = iterator.next();
if (integer == 5) {
iterator.remove(); // safe removal
}
}
System.out.println(list);
}This prints [0, 1, 2, 3, 4, 6, 7, 8, 9] without error.
Another alternative is CopyOnWriteArrayList, which creates a fresh copy of the underlying array on each write, thus isolating the iterator from concurrent modifications:
public static void main(String[] args) {
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer integer = iterator.next();
if (integer == 5) {
list.remove(Integer.valueOf(5)); // safe removal
}
}
System.out.println(list);
}While this also works, it incurs extra memory and copying overhead, which is why it is less commonly recommended.
In summary, the foreach loop cannot safely add or remove elements because it relies on a fail‑fast iterator that checks for structural changes via modCount. Using the iterator's remove method or a CopyOnWriteArrayList are the proper ways to modify a collection during iteration.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
