JDK-8151423 : No java.util.ConcurrentModificationException thrown when removing from a java.util.ArrayList in a for-each loop
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Affected Version: 8u74
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2016-03-07
  • Updated: 2021-01-27
  • Resolved: 2021-01-27
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux test-pc 3.19.0-51-generic #58~14.04.1-Ubuntu SMP Fri Feb 26 22:02:58 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
The following snippet of code doesn't throw a ConcurrentModificationException

List<String> list = new ArrayList<>();
list.add("0");
list.add("1");
list.add("2");
list.add("3");

for (String el : list) {
    if (el.equals("2")) {
        list.remove(el);
    }
    System.out.println(el);
}

Note that LinkedList exposes the same behavior (also for the last element) although it does a "less than" check in hasNext() instead of the not equals check in java.util.ArrayList:L846. 

Although it is stated in https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html that this exception thrown is not guaranteed, I believe that it has to be documented that the fail-fast behavior cannot be guaranteed  even in cases where there are not multiple threads modifying the list (or change the behavior of the snippet above).

The unexpected behavior in the above snippet is that the last element is never accessed but no exception is thrown

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
List<String> list = new ArrayList<>();
list.add("0");
list.add("1");
list.add("2");
list.add("3");

for (String el : list) {
    if (el.equals("2")) {
        list.remove(el);
    }
    System.out.println(el);
}

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
0
1
2
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
ACTUAL -
0
1
2

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.ArrayList;
import java.util.List;

public class ArrayListTest {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("0");
        list.add("1");
        list.add("2");
        list.add("3");

        for (String el : list) {
            if (el.equals("2")) {
                list.remove(el);
            }
            System.out.println(el);
        }
    }
}
---------- END SOURCE ----------

SUPPORT :
YES


Comments
This is a duplicate of JDK-8136821. The API mentions "Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis.Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs. " The following comment from the JDK-8136821, explains the reason for closing this as not an issue: " Modifying a collection while it's being iterated is considered to be a programming error. The detection of this situation and the consequent throwing of ConcurrentModificationException is performed on a "best-effort" basis. It's improper for a program to rely on CME to be thrown in particular cirumstances. This is described in the class specification of ArrayList, and similar wording appears in the specification of other core collections, such as HashMap. The prevailing style of the core collections is to check for concurrent modification only during an Iterator's next() call but not within its hasNext() call. ArrayList conforms to this style. "
08-03-2016