I know that if the Collection will be changed while some thread is traversing over it using iterator, the iterator.next() will throw a ConcurrentModificationException.
But it shows different behavior depending on the number of elements in the list.
I tried a code snippet in which I traversed a list in for-each loop and in between it's traversal removed an element from the list using remove() method of the list.
Ideally it should throw a ConcurrentModificationException in this condition without depending on number of elements in the list, But it is not true when number of elements in list are two.
Case 1: Number of elements in list - 1
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("One");
for (String string : list)
{
System.out.println(string);
list.remove(string);
}
}
Output: One
Exception in thread "main" java.util.ConcurrentModificationException
That's was as expected.
Case 2: Number of elements in list - 2
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("One");
list.add("two");
for (String string : list)
{
System.out.println(string);
list.remove(string);
}
}
Output: One
No Exception is thrown?????????
Case 3: Number of elements in list - 3
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("One");
list.add("Two");
list.add("Three");
for (String string : list)
{
System.out.println(string);
list.remove(string);
}
}
Output: One
Exception in thread "main" java.util.ConcurrentModificationException
Again an exception is thrown which is ideal behavior.
But why it runs properly in case-2 without throwing any ConcurrentModificationException.
From the documentation (emphasis mine):
The iterators returned by this class's
iterator
andlistIterator
methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's ownremove
oradd
methods, the 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.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.
The previous answers posted show you the relevant docs that explain why this is appropriate behavior; you are not guaranteed to receive that exception.
If you're really interested in seeing why you don't receive it with only two elements (or really, if you remove the next to last element regardless of the size), you can look at the source code for ArrayList
.
Your loop:
for (String string : list)
is actually:
for(Iterator<String> i = list.iterator(); i.hasNext(); ) {
String string = i.next();
...
}
Internally in Arraylist there is an int size
that represents the current number of elements in the ArrayList
. It is decremented when you call remove()
.
Inside the Iterator
there is an int cursor
that represents the current location (index) of the Iterator
. It gets incremented when you call next()
hasNext()
in the Iterator
checks the current cursor position against the size.
In your example the chain of events is as follows:
cursor
starts at 0
, size
starts at 2
next()
is called, cursor
incremented to 1
. remove()
is called, size
is decremented to 1
hasNext()
compares cursor
to size
, finds they are the same, returns false
So if you remove the next to last element in any size ArrayList
while iterating through it you will not receive an exception. (Also worth noting is that you will never process the last element in that list in the loop; your example only prints One
for that reason).
Remember though - this is an implementation detail and is not guaranteed. The docs tell you that you should not rely on the exception being thrown (or not thrown). ArrayList
could be rewritten in a different way where the above no longer applies and the exception is thrown (and in fact, in another JVM that may well already be the case).
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