Removing an item from a list in a counting loop against an iterator

Why is this legal:

for(int i=0; i < arr.size(); i++) { arr.remove(i); } 

But using an iterator or syntactic sugar a for each result, a ConcurrentModificationException is ConcurrentModificationException :

 for(String myString : arr) { arr.remove(myString); } 
  • Before everyone starts jumping on the bandwagon, saying that I use iterator.remove(); , I ask why the different behavior, and not as an exception mod Conc. Thank you
+5
source share
3 answers

Let's see how, for example, the ArrayLists 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(); 

Delete the first element

 list.remove(0); 

If we want to call it.remove() now, the iterator will remove number 2 because it now indicates that the field is lastRet .

 if (item == 1) { it.remove(); // list contains 3, 4 } 

That would be the wrong behavior! The iterator's contract states that remove() removes the last element returned by next() , but it cannot hold on to its contract if there are parallel modifications. Therefore, he prefers to be safe and excluded.

The situation may be even more complicated for other collections. If you change the HashMap , it can increase or decrease as necessary. At this time, the elements will fall into different buckets, and an iterator that stores a pointer to the bucket before the reboot is completely lost.

Note that iterator.remove() does not throw an exception on its own, as it can update both the internal state and the collection. A call to remove() on two iterators of the same collection of instances will throw, however, because it will leave one of the iterators in an inconsistent state.

+3
source

Looking at your code, I assume arr is a list. In the top loop, you work directly in the list and “calibrate” your state at the top when you check

 i < arr.size() 

So, if you delete the item, I have to compare it with a lower value. On the other hand, in the second case, you work with the collection after the iterator has been created, and in fact do not calibrate yourself.

Hope this helps.

+2
source

In the first, you modify an array that is not used as an iterator in a for loop.

In the second, you are trying to access the array, which it will be modified, at the same time when you repeat it in a loop. This is why it throws a ConcurrentModificationException .

+1
source

All Articles