java – Removing list item in foreach loop doesn't throw ConcurrentModificationException, why?

Question:

Let's say I have some ArrayList .

ArrayList<Integer> list = new ArrayList<>();

for (int i = 0; i < 10; i++)
  list.add((int) (Math.random() * 20));

And I want to remove all numbers greater than 10 from it.

You can do this "correctly" through an iterator, getting a guaranteed result.

for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext(); )
    if (iterator.next() > 10)
        iterator.remove();

But to my surprise, the option also works correctly:

for (Integer i : list)
    if (i > 10)
        list.remove(i);

Even though it says everywhere that any attempt to remove from a collection in a loop without using an iterator results in a ConcurrentModificationException .

And, indeed, if we delete unconditionally, we will get just ConcurrentModificationException

for (Integer i : list)
      list.remove(i);

Actually, can someone explain the magic or give a tip where you can read on this topic?

Answer:

You seem to be incredibly lucky:

  • you didn't have elements whose value was greater than 10
  • you had an element whose value was greater than 10, but it was only one and was located in the penultimate place in the list

Let's work through a shorter example.

ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(22);
list.add(3);

We have added three numbers. So how does foreach work

  1. It gets an iterator.
  2. Checks for the presence of the next element hasNext() .

     public boolean hasNext() { return cursor != size(); // cursor is zero initially. }
  3. If it returns true , then take the next element with next() .

     public E next() { checkForComodification(); try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } final void checkForComodification() { // Initially modCount = expectedModCount (our case 5) if (modCount != expectedModCount) throw new ConcurrentModificationException(); }

Then steps 2 and 3 are repeated until hasNext() returns false .

If you remove an element from the list, then its size will decrease and modCount will increase.

If an element is removed during iteration, a ConcurrentModificationException will be thrown at the line modCount != expectedModCount .


But what happens if the penultimate element is removed?

> cursor = 0 size = 3 --> hasNext() успешно и next() тоже без эксепшена
> cursor = 1 size = 3 --> hasNext() успешно и next() тоже без эксепшена

When we remove the value 22, the size will decrease to 2.

> cursor = 2 size = 2 --> hasNext() не успешно и next() пропускается.

Otherwise, a ConcurrentModificationException will be thrown due to modCount != expectedModCount .

And in this single case, the test will pass with a bang ..

Here is the magic …. Or a bug ….

Scroll to Top