Why is this legal:
for(int i=0; i < arr.size(); i++) {
arr.remove(i);
}
But using an iterator or the syntactic sugar of a for each results in a ConcurrentModificationException
:
for(String myString : arr) {
arr.remove(myString);
}
iterator.remove();
I'm asking why the different behavior, not how to avoid the conc mod exception. Thanks. An element can be removed from a Collection using the Iterator method remove(). This method removes the current element in the Collection. If the remove() method is not preceded by the next() method, then the exception IllegalStateException is thrown.
The right way to remove objects from ArrayList while iterating over it is by using the Iterator's remove() method. When you use iterator's remove() method, ConcurrentModfiicationException is not thrown.
If we remove an item from an ArrayList while iterating it, the list. remove(s) will throws java. util. ConcurrentModificationException .
Iterating over a List is much much faster than iterating over a set. The currently accepted answer is using a very small set and list and hence, the difference is negligible there.
Let's take a look at how, e.g., ArrayLists
's iterator is implemented:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
public E next() {
checkForComodification();
int i = cursor;
if (i >= size) throw new NoSuchElementException();
// ...
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
// ...
ArrayList.this.remove(lastRet);
// ...
cursor = lastRet;
lastRet = -1;
}
Let's look at an example:
List list = new ArrayList(Arrays.asList(1, 2, 3, 4));
Iterator it = list.iterator();
Integer item = it.next();
We remove the first element
list.remove(0);
If we want to call it.remove()
now, the iterator would remove number 2 because that's what field lastRet
points to now.
if (item == 1) {
it.remove(); // list contains 3, 4
}
This would be incorrect behavior! The contract of the iterator states that remove()
deletes the last element returned by next()
but it couldn't hold its contract in the presence of concurrent modifications. Therefore it chooses to be on the safe side and throw an exception.
The situation may be even more complex for other collections. If you modify a HashMap
, it may grow or shrink as needed. At that time, elements would fall to different buckets and an iterator keeping pointer to a bucket before rehashing would be completely lost.
Notice that iterator.remove()
doesn't throw an exception by itself because it is able to update both the internal state of itself and the collection. Calling remove()
on two iterators of the same instance collection would throw, however, because it would leave one of the iterators in an inconsistent state.
Looking at your code, I am assuming arr is a List. In the top loop you operate on the list directly, and "re-calibrate" your condition at the top when you check
i < arr.size()
So if you remove an element, i has to compare to a lesser value. On the other hand, in the second case you operate on the collection after an iterator has been instantiated, and don't really re-calibrate yourself.
Hope this helps.
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