As a general rule, ConcurrentModificationException
s are thrown when the modification is detected, not caused. If you never access the iterator after the modification, it won’t throw an exception. This minute detail makes ConcurrentModificationException
s rather unreliable for detecting misuse of data structures, unfortunately, as they only are thrown after the damage has been done.
This scenario doesn’t throw a ConcurrentModificationException
because next()
doesn’t get called on the created iterator after the modification.
For-each loops are really iterators, so your code actually looks like this:
List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iter = strings.iterator();
while(iter.hasNext()){
String string = iter.next();
if ("B".equals(string))
strings.remove("B");
}
System.out.println(strings);
Consider your code running on the list you provided. The iterations look like:
hasNext()
returns true, enter loop, -> iter moves to index 0, string = “A”, not removedhasNext()
returns true, continue loop -> iter moves to index 1, string = “B”, removed.strings
now has length 2.hasNext()
returns false (iter is currently at the last index, no more indices to go), exit loop.
Thus, as ConcurrentModificationException
s are thrown when a call to next()
detects a that a modification has been made, this scenario narrowly avoids such an exception.
For your other two results, we do get exceptions. For "A", "B", "C", "D"
, after removing “B” we are still in the loop, and next()
detects the ConcurrentModificationException
, whereas for "A", "B"
I’d imagine it’s some kind of ArrayIndexOutOfBounds that’s being caught and re-thrown as a ConcurrentModificationException