Consider the following code snippet:
List<String> list = new LinkedList<>();
list.add("Hello");
list.add("My");
list.add("Son");
for (String s: list){
if (s.equals("My")) list.remove(s);
System.out.printf("s=%s, list=%s\n",s,list.toString());
}
This results in output:
s=Hello, list=[Hello, My, Son]
s=My, list=[Hello, Son]
So clearly the loop was only entered twice, and the third element, "Son", never gets visited. From the underlying library code, it looks like what happens is that the hasNext()
method in the iterator doesn't check for concurrent modification, only the size against the next index. Since the size has been reduced by 1 by the remove()
call, the loop simply doesn't get entered again, but no ConcurrentModificationException is thrown.
This seems to contradict the contract of the iterator:
The list-iterator is fail-fast: if the list is structurally modified at any time after the Iterator is created, in any way except through the list-iterator's own
remove
oradd
methods, the list-iterator will throw aConcurrentModificationException
. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Is this a bug? Again, the contract of the iterator definitely appears to be disobeyed here - the structure of the list is structurally modified by something other than the iterator in the middle of iteration.