Различные результаты с Итератором для Java Framework Collection - PullRequest
1 голос
/ 06 апреля 2019

У меня есть несколько вопросов о различном поведении Iterator в основных классах Java Framework Collection (не только для класса List).

  1. List

Если янапишите для каждого у меня будет исключение:

Collection<String> cc= new ArrayList<>(3);
        cc.add("Cio");
        cc.add("Mio");
        cc.add("Tio");
for (String s:cc) {
    System.out.println(s);
    cc.remove(s);            //Exception
}

Если я использую Итератор, у меня не будет исключения:

for (Iterator<String> it =cc.iterator(); it.hasNext();) {
    String s =it.next();
    if (s.startsWith("C"))
        it.remove();
}
ArrayDeque

Это отличается от ArrayDeque, но если я использую для каждого, у меня не будет исключения:

ArrayDeque<String> greetings = new ArrayDeque<String>();
        greetings.push("hello");
        greetings.push("hi");
        greetings.push("ola");
        greetings.pop(); 
        greetings.peek();
        while (greetings.peek() != null)
        System.out.print(greetings.pop());

Но если я использую итератор,У меня будет исключение:

Iterator<String> it = greetings.iterator();
        while(it.hasNext()) {
            System.out.println(greetings.pop()); //Exception
        }

Почему?И генерирует ли итератор исключение для других коллекций JFC, в частности: HashSet, TreeSet, LinkedList?

Большое спасибо!

A.

1 Ответ

2 голосов
/ 06 апреля 2019

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).Это не может быть структурной модификацией.Так что это не исключение.

...