В чем разница Iterator и ListIterator в Java
Что проверяют
Знаете ли вы, откуда берётся ConcurrentModificationException и как правильно удалять элементы во время обхода. Сам вопрос простой, но за ним обычно идёт именно это.
Как отвечать
Iterator это базовый однонаправленный обход любой коллекции: hasNext, next и remove. ListIterator доступен только у списков и расширяет его:
- движение в обе стороны:
hasPreviousиprevious; - позиции:
nextIndexиpreviousIndex; - изменение содержимого:
setзаменяет последний возвращённый элемент,addвставляет новый в текущую позицию; - старт с произвольного места:
list.listIterator(index).
Ключевое практическое отличие: ListIterator умеет менять список во время обхода, а обычный Iterator только удалять.
Дальше стоит сказать про безопасность обхода. Коллекции из java.util дают итераторы, которые следят за счётчиком изменений: если изменить коллекцию мимо итератора во время цикла, следующий next бросит ConcurrentModificationException. Поэтому удаление внутри for-each запрещено, а правильный способ такой:
Iterator<Order> it = orders.iterator();
while (it.hasNext()) {
if (it.next().isExpired()) it.remove(); // безопасно
}
orders.removeIf(Order::isExpired); // короче, с Java 8Полезно добавить, что у конкурентных коллекций итераторы слабо согласованные и исключения не бросают, зато могут не увидеть часть изменений.
Где спотыкаются
Удаляют из списка в цикле for-each и ловят исключение.
Считают, что ConcurrentModificationException бывает только в многопоточном коде. Он прекрасно воспроизводится в одном потоке.
Не знают про removeIf, который закрывает большинство таких случаев одной строкой.