JDK-8136821 : ConcurrentModificationException not thrown when removing the next to last element , less than expected number of iterations
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Affected Version: 7u80,8,9
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • Submitted: 2015-06-20
  • Updated: 2021-05-12
  • Resolved: 2015-10-06
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
The method
java.util.AbstractList.Itr.hasNext() 
does not check for modification.

That leads in one missing iteration loop when removing the next to last element.

Assumtion is that when "for each" over an list with 3 elements the loop is performed exactly 3 times or a ConcurrentModificationException is thrown.

When performing the next code the loop is iterated 2 times only and no exception is coming up:

	@Test
	public void testRemove2ndOf3() {
		List<String> tempArrayList = new ArrayList<String>();
		tempArrayList.add("1");
		tempArrayList.add("2");
		tempArrayList.add("3");

		List<String> tempIterated = new ArrayList<String>();
		for (String tempString : tempArrayList) {
			tempIterated.add(tempString);
			if (tempString.equals("2")) {
				tempArrayList.remove(tempString);
				// 3 is still in the list
			}
		}
		assertEquals("Expected to iterate 3 times, but found only " + tempIterated, 3, tempIterated.size());
		// Normally fail("Expected ConcurrentModificationException");
	}


Currently loop is performed without exception but the checking List tempIterated contains [1,2] only.


When removing the "1" element instead of "2" the above code throws an ConcurrentModificationException as expected.

ADDITIONAL REGRESSION INFORMATION: 
java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Perform the test testRemove2ndOf3 specified in the Description.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
ConcurrentModificationException
ACTUAL -
Loop is iterated 2 times and terminates normallly (even there were 3 elements in)

REPRODUCIBILITY :
This bug can be reproduced always.

CUSTOMER SUBMITTED WORKAROUND :
Do not remove in an foreach loop at all (is best practice anyway)


Comments
A somewhat odd pathology that ArrayList exhibits is that removing elements such that the iterator is now positioned exactly at the end will cause a loop to terminate normally instead of throwing ConcurrentModificationException. This occurs, for example, if the penultimate element is removed from a list while it's being iterated: var orig = IntStream.range(0, 10).mapToObj(String::valueOf).toList() orig ==> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for (String s : orig) { var temp = new ArrayList<>(orig); System.out.print(s + ": "); try { for (String t : temp) { if (s.equals(t)) { temp.remove(s); } } System.out.println("ok"); } catch (ConcurrentModificationException cme) { System.out.println(cme); } } 0: java.util.ConcurrentModificationException 1: java.util.ConcurrentModificationException 2: java.util.ConcurrentModificationException 3: java.util.ConcurrentModificationException 4: java.util.ConcurrentModificationException 5: java.util.ConcurrentModificationException 6: java.util.ConcurrentModificationException 7: java.util.ConcurrentModificationException 8: ok 9: java.util.ConcurrentModificationException This also occurs if an iterator is taken from a non-empty list, the list is cleared, and then the iterator is iterated: var temp = new ArrayList<>(orig); var it = temp.iterator(); temp.clear(); it.hasNext() ==> false Another example occurs if enough elements are removed from earlier in the list such that the current position is exactly at the end: var temp = new ArrayList<>(orig) temp ==> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for (String s : temp) { if (s.equals("6")) { temp.subList(0, 3).clear(); } } // no ConcurrentModificationException For some reason this particular pathology seems to attract people's attention, and a lot of bugs have been filed about "missing" ConcurrentModificationExceptions. As stated previously, CME is thrown on a "best-effort" basis, and it's not guaranteed to be thrown in all cases of concurrent modification. Therefore, applications mustn't depend on this behavior.
27-01-2021

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. Closing as Won't Fix.
06-10-2015

Similar to, but not the same as, JDK-8114832.
06-10-2015

Related bugs JDK-4902078, JDK-6687277. 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. " So, the test case should not be treated as a bug as per the above API documentation. Moving to dev-team for their evaluation.
21-09-2015

Pardeep, Try to reproduce the issue with 8u40, 8u60 and 9 ea builds.
22-06-2015