Why is there no ConcurrentModificationException in this situation with the LinkedList iterator?

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 leads to the conclusion:

s = Hello, list = [Hello, My, Son]
s = My, list = [Hello, Son]

Thus, the cycle was introduced only twice, and the third element "Son" is never visited. From the base code of the library, it seems that the hasNext() method in the iterator does not check the simultaneous modification, but only the size with respect to the next index. Since the size was reduced by 1 by calling remove() , the loop simply is not thrown again, but the ConcurrentModificationException is not thrown.

This is contrary to the iterator contract:

Fail-fast iterator list: if the list is structurally modified at any time after creating the Iterator, in any way other than through its own remove or add iterator list, the iterator list will throw a ConcurrentModificationException . Thus, in the face of simultaneous modification, the iterator is fast and clean, and does not risk arbitrary, non-deterministic behavior at an undetermined time in the future.

This is mistake? Again, the iterator contract is definitely not in dispute here - the list structure is structurally modified by something other than an iterator in the middle of the iteration.

+5
source share
2 answers

Read Javadoc at the class level:

Please note that the fault-tolerant behavior of the iterator cannot be guaranteed, since, generally speaking, it is impossible to make any serious guarantees in the presence of an unsynchronized parallel modification. Unmanaged iterators throw a ConcurrentModificationException with maximum efficiency. Therefore, it would be wrong to write a program that depends on this exception for its correctness: the unsuccessful behavior of iterators should only be used to detect errors.

+3
source

From https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html :

Note that unsafe iterator behavior cannot be guaranteed since, generally speaking, it is not possible to make any firm guarantees in the presence of an unsynchronized parallel modification. Normal-fast iterators throw a ConcurrentModificationException on the best effort base. Therefore, it would be wrong to write a program that depends on this exception for its correctness: fault-tolerant behavior iterators should only be used to detect errors.

That is, the iterator will try his best to exclude the exception, but in all cases this is not guaranteed.

Here are some more links to how fast iterators work, and how they are implemented - in case someone is interested:

http://www.certpal.com/blogs/2009/09/iterators-fail-fast-vs-fail-safe/

http://javahungry.blogspot.com/2014/04/fail-fast-iterator-vs-fail-safe-iterator-difference-with-example-in-java.html

http://www.javaperformancetuning.com/articles/fastfail2.shtml

And here is another question in which people try to find out the same thing:

Why doesn't this code throw a ConcurrentModificationException?

+3
source

Source: https://habr.com/ru/post/1207911/


All Articles