ArrayList
В списке содержится поле modCount
, которое увеличивается каждый раз, когда в список вносятся структурные изменения.
Структурные изменения - это изменения, которые изменяют размер списка или иным образом нарушают его таким образом, что выполняемые итерации могут давать неправильные результаты.
Далее ...
Если значение этого поля неожиданно изменится, итератор (или итератор списка) сгенерирует исключение ConcurrentModificationException в ответ на следующие операции, операции удаления, предыдущего, установки или добавления.Это обеспечивает отказоустойчивое поведение, а не недетерминированное поведение при одновременной модификации во время итерации. Использование этого поля подклассами необязательно.
Если подкласс хочет предоставить отказоустойчивые итераторы (и списочные итераторы), то он просто должен увеличить это поле в своих методах add (int, E) и remove (int) (и любых других методах, которыеон переопределяет то, что приводит к структурной модификации списка.
Два кода кода итерации списка:
1.
for (String s:str1) {
System.out.println(s);
str1.remove(s);
}
и
2.
Iterator<String> i1 = str.iterator();
while(i1.hasNext()) {
i1.next();
i1.remove();
}
- может показаться одинаково, но внутренне немного отличается.
Стоит отметить, что итератор списка содержит expectedModCount
. Это должно быть всинхронизировать с modCount
при изменении списка во время итерации.
В 1-м случае String s:str1
получает итератор, проверяет hasNext () и вызывает next (), как во 2-м случае.в методе remove (). str1.remove(s);
вызывает метод удаления ArrayList. Это увеличивает modCount
, но не expectedModCount
. Так что во второй итерации, когда вызывается next (), он вызывает исключение ConcurrentModificationException. С другой стороныво 2-м случае i1.remove();
вызывает метод удаления из реализации Iterator в ArrayList.Это увеличивает значения modCount
и expectedModCount
и - Bingo .
Примечание: Пропуск i1.next();
во втором сценарии вызовет IllegalStateExcepton.Это связано с тем, что курсор для следующего элемента в списке не обновляется.
TakeAway: Не вызывайте метод list.remove(element)
во время итерации списка.Этот метод предназначен для вызова, когда он не входит в итерацию.
ArrayDeque
Если вы выполняете итерацию ArrayDeque следующим образом:
Iterator<String> i1 = str.iterator();
while(i1.hasNext()) {
i1.next();
i1.remove();
}
-- он работает точно так же, как и его ArrayList
аналог.
При вызове метода pop()
или push()
класса ArrayDeque вы фактически не выполняете итерацию в очереди, вы просто изменяете головуили хвост очереди.Это похоже на вызов метода remove () класса ArrayList, когда он не находится в Iteration (не в методе remove () Iterator из ArrayList).Это не может быть структурной модификацией.Так что это не исключение.