Как 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();){
...
}