问题
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.
回答1:
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.
回答2:
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?
来源:https://stackoverflow.com/questions/27195826/why-no-concurrentmodificationexception-in-this-situation-with-linkedlist-iterato