Как Iterator удалить метод на самом деле удалить объект

Мы все знаем, что самый безопасный "и, возможно, только безопасный" способ удаления объекта из коллекции при его итерации - сначала получить Iterator, выполнить цикл и удалить при необходимости;

Iterator iter=Collection.iterator();
while(iter.hasNext()){
    Object o=iter.next()
    if(o.equals(what i'm looking for)){
        iter.remove();
    }
}

То, что я хотел бы понять и, к сожалению, не нашел глубокого технического объяснения, заключается в том, как это удаление выполняется,
Если:

for(Object o:myCollection().getObjects()){
    if(o.equals(what i'm looking for)){
        myCollection.remove(o);
    }
}

Будут бросать ConcurrentModificationException, что делает "в технических терминах" Iterator.remove() делать? Удаляет ли объект, прерывает цикл и перезапускает цикл?

Я вижу в официальной документации:

"Удаляет текущий элемент. Броски IllegalStateException, если делается попытка вызвать remove(), которому не предшествует вызов next()."

Часть "удаляет текущий элемент", заставляет меня думать о той же самой ситуации, что происходит в "регулярном" цикле = > (выполнить тест равенства и удалить, если необходимо), но почему цикл Iterator ConcurrentModification-safe?

Ответ 1

Как именно Итератор удаляет элементы, зависит от его реализации, которые могут отличаться для разных Коллекций. Определенно это не нарушает цикл, в котором вы находитесь. Я просто посмотрел, как выполняется итератор ArrayList, и вот код:

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

Таким образом, он проверяет наличие параллельных изменений, удаляет элемент с использованием общедоступного метода удаления ArrayList и увеличивает счетчик изменений списка, поэтому ConcurrentModificationException не будет выбрано на следующей итерации.

Ответ 2

Причина, по которой вы не можете изменить список во время итерации по нему, заключается в том, что итератору необходимо знать, что возвращать для hasNext() и next().

Как это делается, зависит от реализации, но вы можете посмотреть исходный код ArrayList/AbstractList/LinkedList и т.д.

Также обратите внимание, что в некоторых ситуациях вы можете использовать некоторый код, подобный этому, в качестве альтернативы:

List<Foo> copyList = new ArrayList<>(origList);
for (Foo foo : copyList){
  if (condition){
    origList.remove(foo);
  }
}

Но этот код, вероятно, будет работать немного медленнее, потому что сбор должен быть скопирован (только мелкая копия), и элемент для удаления должен быть найден.

Также обратите внимание, что если вы используете итератор напрямую, рекомендуется использовать цикл for вместо цикла while, поскольку это ограничивает область действия переменной:

for (Iterator<Foo> iterator = myCollection.iterator(); iterator.hasNext();){
...
}