Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why no ConcurrentModificationException in this situation with LinkedList iterator? [duplicate]

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 or add methods, the list-iterator will throw a ConcurrentModificationException. 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.

like image 831
PeteyPabPro Avatar asked Nov 28 '14 20:11

PeteyPabPro


People also ask

How can you avoid ConcurrentModificationException while iterating a collection?

To Avoid ConcurrentModificationException in single-threaded environment. You can use the iterator remove() function to remove the object from underlying collection object. But in this case, you can remove the same object and not any other object from the list.

Does iterator throw a ConcurrentModificationException?

If a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this ConcurrentModificationException.

How do I fix ConcurrentModificationException?

How do you fix Java's ConcurrentModificationException? There are two basic approaches: Do not make any changes to a collection while an Iterator loops through it. If you can't stop the underlying collection from being modified during iteration, create a clone of the target data structure and iterate through the clone.

Why does concurrent modification exception occur?

What Causes ConcurrentModificationException. The ConcurrentModificationException generally occurs when working with Java Collections. The Collection classes in Java are very fail-fast and if they are attempted to be modified while a thread is iterating over it, a ConcurrentModificationException is thrown.


2 Answers

Read the class-level Javadoc:

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.

like image 141
Louis Wasserman Avatar answered Oct 05 '22 18:10

Louis Wasserman


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

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.

That is, the iterator will try its best to throw exception, but isn't guaranteed to do so in all cases.

Here are some more links on how fail fast iterators works and how the are implemented - in case someone will be 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 SO question where people trying to find out the same thing:

Why isn't this code causing a ConcurrentModificationException?

like image 27
striving_coder Avatar answered Oct 05 '22 20:10

striving_coder