I encountered this piece a of code, that removes item from list while iterating, and yet it does not throw concurrent exception and leaves some items in the list.
Why is that?
Example:.
public class Application {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
for (Integer integer : integerList) {
removeInteger(integerList, integer);
}
System.out.println(integerList.toString()); // prints [2] as it was not removed (while it should be)
}
private static void removeInteger(List<Integer> integerList, Integer integer) {
for (Integer integer1 : integerList) {
if (integer.equals(integer1)) {
int index = integerList.indexOf(integer);
if (index >= 0) {
integerList.remove(index);
}
}
}
}
}
If I change removeInteger
method to use Iterator
instead and in main
I pass a copy of integerList
everything works as expected.
First of all if you add some more elements to the list it will still fail with a ConcurrentModificationException.
For the reason that it does not fail with the posted example, we have to take a look at what happens internally.
If you debug you see that in the posted example, both loops are only iterated once. Removing the element in the inner loop (removeInteger
method) exhausts both iterators. As there is no further interaction, no error is caused. 'Only' a call of Iterator#next
after a modification could cause a ConcurrentModificationException.
Yes, but why does it exhaust them?
Checking the source code of ArrayList#Itr
we can see that
public boolean hasNext() {
return cursor != size;
}
only checks a variable called size
. This variable size
is a property of the ArrayList
. When remove
is called this variable is decremented. Hence the next time hasNext
is called, the Iterator assumes there are no more new elements.
SideNote: cursor
points at the next element in the list.