可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have the below code and I would expect it to throw a ConcurrentModificationException
, but it runs successfully. Why does this happen?
public void fun(){
List <Integer>lis = new ArrayList<Integer>();
lis.add(1);
lis.add(2);
for(Integer st:lis){
lis.remove(1);
System.out.println(lis.size());
}
}
public static void main(String[] args) {
test t = new test();
t.fun();
}
回答1:
The remove(int)
method on List
removes the element at the specified position. Before you start your loop, your list looks like this:
[1, 2]
Then you start an iterator on the list:
[1, 2]
^
Your for
loop then removes the element at position 1, which is the number 2:
[1]
^
The iterator, on the next implied hasNext()
call, returns false
, and the loop terminates.
You will get a ConcurrentModificationException
if you add more elements to the list. Then the implicit next()
will throw.
As a note, from the Javadoc for ArrayList
from the JCF:
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.
This is probably actually a bug in the Oracle ArrayList
iterator implementation; hasNext()
does not check for modification:
public boolean hasNext() {
return cursor != size;
}
回答2:
It doesn't throw a ConcurrentModificationException because, as vandale said, the iterator only checks for comodification on next(). Here's a portion of the Iterator instance returned by an ArrayList:
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
hasNext() simply looks to see if the cursor is pointing at the last index of the list. It doesn't check to see if the list was modified. Because of this you don't get an ConcurrentModificationException, it just stops iterating.
回答3:
if you have a list of 3 like:
lis.add(1);
lis.add(2);
lis.add(3);
you will get ConcurrentModificationException in your case.
PS: I have tried this!
回答4:
Because you aren't removing 1, you are removing the element at 1. ( remove(int)
vs remove(Object)
)
The iterator will only check for modification on a call to next()
and not hasNext()
, and the loop will exit after the call to hasNext()
because you have removed 2, the list is only one long and thus exits.
回答5:
The gist of the matter is, as stated on both ArrayList
and ConcurrentModificationException
:
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.
Now a code sample from the Iterator
returned by ArrayList
:
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();
<stuff>
return <things>;
}
<more methods>
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
As you can clearly see, in the case of ArrayList
the "best effort" is in checking for modifications when calling next()
and not when calling getNext()
. Your loop terminates without calling next()
a second time, hence no exception. Should you have 3 elements to begin with, or add an element it will fail. Also worth noting that if you modify the array list using reflection without updating the modCount
variable (naughty...), then the exception will not be thrown at all. modCount
is also not volatile, which again shows it's only best effort and has no guarantees, since the iterator might not see the latest value anyway.
回答6:
In this loop:
for(Integer st:lis){
lis.remove(1);
System.out.println(lis.size());
}
You are only constantly removing element with index 1 from the matrix without even caring what is in st. So this loop and with every iteration will try to remove item with index 1. Concurent modification will come up with tthis loop:
for(Integer st:lis){
lis.remove(st);
System.out.println(lis.size());
}
回答7:
You have only 2 entries in the list. So, the loop runs only once since you are removing an entry.
ConcurrentModificationException will be thrown if a list is modified and again you try to do some operation on it. But before doing any operation on it, we are out of the loop hence no exception. Try adding another entry into list and run your program which will throw exception.