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();
}
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.
The ConcurrentModificationException is used to fail-fast when something being iterated on is modified. This exception occurs when an object is attempted to be modified concurrently without permission.
Class ConcurrentModificationException. This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible. For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.
If we invoke a sequence of methods on an object that violates its contract, then the object throws ConcurrentModificationException. For example: if while iterating over the collection, we directly try to modify that collection, then the given fail-fast iterator will throw this ConcurrentModificationException.
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;
}
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.
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!
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With