Короткий ответ
Fail-fast итератор работает с самой коллекцией и при любом структурном изменении (добавление или удаление элементов), выполненном не через сам итератор, немедленно выбрасывает ConcurrentModificationException. Fail-safe — точнее, слабо согласованные (weakly consistent) итераторы из java.util.concurrent — не бросают исключение при параллельных изменениях, но не гарантируют, что увидят изменения, сделанные после создания итератора.
Как это работает подробнее
Механизм fail-fast реализован через счётчик модификаций modCount: итератор запоминает его при создании и сравнивает при каждом вызове next(); если значения разошлись — коллекция менялась «на стороне» — итератор «быстро падает» с исключением. Так ведут себя коллекции пакета java.util: ArrayList, HashMap, LinkedList. Fail-safe итераторы либо проходят по копии коллекции, либо устроены так, что не бросают исключение; плата — дополнительные расходы памяти на копирование или менее строгие данные.
- Примеры fail-safe: итераторы ConcurrentHashMap, CopyOnWriteArrayList, ConcurrentLinkedQueue.
- Удаление при обходе: в коллекциях java.util изменяйте коллекцию только через метод
remove()самого итератора или собирайте элементы для удаления отдельно.
Пример кода
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.equals("b")) it.remove(); // безопасно: remove самого итератора
}
// list.remove("b") в цикле for-each бросил бы
// ConcurrentModificationException — так работает fail-fastЧто использовать на практике
- Итератор коллекций java.util нельзя совмещать с изменением самой коллекции в том же потоке.
- Для удаления во время обхода используйте
remove()итератора либо копите элементы и удаляйте после цикла.