1. Введение
В этой статье мы представим концепцию Fail-Fast и Fail-Safe Iterators
.
Системы Fail-Fast прерывают работу как можно быстрее, немедленно обнаруживая сбои и останавливая всю операцию.
Принимая во внимание, что отказоустойчивые системы не прерывают операцию в случае сбоя. Такие системы стараются максимально избегать возникновения сбоев.
2. Отказоустойчивые итераторы
Отказоустойчивые итераторы в Java не работают, когда базовая коллекция изменяется.
Коллекции
поддерживают внутренний счетчик под названием modCount
. Каждый раз, когда элемент добавляется или удаляется из коллекции
, этот счетчик увеличивается.
При повторении при каждом вызове next()
текущее значение modCount
сравнивается с начальным значением. В случае несоответствия генерируется исключение ConcurrentModificationException
, которое прерывает всю операцию.
Итераторы по умолчанию для коллекций
из пакета java.util,
таких как ArrayList
, HashMap
и т. д ., являются Fail-Fast.
ArrayList<Integer> numbers = // ...
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
Integer number = iterator.next();
numbers.add(50);
}
В приведенном выше фрагменте кода исключение ConcurrentModificationException
возникает в начале следующего цикла итерации после выполнения модификации.
Поведение Fail-Fast не гарантируется во всех сценариях, поскольку невозможно предсказать поведение в случае одновременных изменений. Эти итераторы генерируют исключение ConcurrentModificationException
по мере возможности .
Если во время итерации над Collection
элемент удаляется с помощью метода iterator
remove()
, это совершенно безопасно и не вызывает исключения .
Однако, если для удаления элемента используется метод remove() объекта
Collection
, возникает исключение: ``
ArrayList<Integer> numbers = // ...
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
if (iterator.next() == 30) {
iterator.remove(); // ok!
}
}
iterator = numbers.iterator();
while (iterator.hasNext()) {
if (iterator.next() == 40) {
numbers.remove(2); // exception
}
}
3. Отказоустойчивые итераторы
Отказоустойчивые итераторы предпочитают отсутствие сбоев неудобствам обработки исключений.
Эти итераторы создают клон фактической коллекции
и перебирают ее. Если какое-либо изменение произойдет после создания итератора, копия останется нетронутой. Следовательно, эти итераторы
продолжают перебирать коллекцию
, даже если она изменена.
Однако важно помнить, что действительно отказоустойчивых итераторов не существует. Правильный термин — слабо согласованный.
Это означает, что если коллекция
изменяется во время итерации, то, что видит итератор
, слабо гарантировано . Это поведение может быть разным для разных коллекций
и задокументировано в Javadocs каждой такой коллекции
.
Однако отказоустойчивые итераторы
имеют несколько недостатков. Одним из недостатков является то, что Iterator
не гарантирует возврат обновленных данных из Collection
, так как он работает с клоном, а не с фактическим Collection
.
Другим недостатком являются накладные расходы на создание копии Collection
как по времени, так и по памяти.
Итераторы
для коллекций
из пакета java.util.concurrent
, такие как ConcurrentHashMap
, CopyOnWriteArrayList
и т. д., по своей природе являются отказоустойчивыми.
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("First", 10);
map.put("Second", 20);
map.put("Third", 30);
map.put("Fourth", 40);
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
map.put("Fifth", 50);
}
В приведенном выше фрагменте кода мы используем Fail-Safe Iterator
. Следовательно, несмотря на то, что новый элемент добавляется в коллекцию
во время итерации, он не генерирует исключение.
Итератор по умолчанию `для
ConcurrentHashMap слабо согласован. Это означает, что этот
итератор может допускать одновременную модификацию, перемещаться по элементам в том виде, в каком они существовали на момент создания
Iterator , и может (но не обязательно) отражать изменения в
Collection после создания
Iterator` .
Следовательно, в приведенном выше фрагменте кода итерация повторяется пять раз, что означает обнаружение нового добавленного элемента в Collection
.
4. Вывод
В этом руководстве мы увидели, что означают отказоустойчивые и отказоустойчивые итераторы
и как они реализованы в Java.
Полный код, представленный в этой статье, доступен на GitHub .