List бросает ConcurrentModificationException, но не задает ConcurrentModificationException?

У меня ниже двух классов java

import java.util.*;

public class ArrayListTest032 {
    public static void main(String[] ar) {
        List<String> list = new ArrayList<String>();
        list.add("core java");
        list.add("php");
        list.add("j2ee");
        list.add("struts");
        list.add("hibernate");

        Iterator<String> itr = list.iterator();

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        list.remove("php");

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }

    }
}

Когда я запускаю код выше, я получаю ниже результата.

core java
php
j2ee
struts
hibernate

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)
    at ArrayListTest032.main(ArrayListTest032.java:20)

Ожидается, что я изменяю список во время итерации. Но ниже класса java такая же логика выполняется множеством семейства.

import java.util.*;

public class HashSetTest021 {
    public static void main(String[] ar) {
        Set<String> set = new HashSet<String>();
        set.add("core java");
        set.add("php");
        set.add("j2ee");
        set.add("struts");
        set.add("hibernate");

        Iterator<String> itr = set.iterator();

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        set.remove("php");

        while (itr.hasNext()) {
            System.out.println(itr.next());
        }

    }
}

И вышло.

hibernate
core java
j2ee
php
struts

Нет ConcurrentModificationException.

Я просто хочу знать, почему тот же кусок кода бросает ConcurrentModificationException в случае семейства list, но в случае set не существует ConcurrentModificationException

Ответ 1

Это различие в реализации: итератор, возвращаемый списком массивов, обнаруживает одновременные модификации, даже когда он позиционируется в конце, потому что он проверяет длину; итераторы HashSet, TreeSet и LinkedList, с другой стороны, не обнаруживают это условие, потому что они проверяют, что они позиционируются в конце, прежде чем проверять одновременную модификацию. Документация позволяет итераторам не выполнять параллельные модификации, поэтому оба подхода действительны.

Ответ 2

Это своего рода "ретроградное" поведение, так как итераторы, когда они полностью пройдены, не могут использоваться повторно, иначе их метод hasNext должен возвращать значение false, когда вы достигнете конца списка.

В этом случае, хотя итератор, возвращаемый ArrayList.iterator, является внутренним классом реализации с кодом для hasNext следующим образом:

public boolean hasNext() {
    return cursor != size;
}

Поэтому, когда вы вызываете hasNext во втором цикле, он указывает (ложно), что есть больше элементов для итерации, потому что вы выполнили операцию, которая изменила размер списка после первой итерации. Семантически, вы не сможете продолжить итерацию над элементами в списке после того, как вы достигнете конца, но из-за этой детали реализации она позволяет вам продолжить второй цикл while. Конечно, в этот момент вы получаете исключение параллельной модификации из-за изменений, внесенных вами в список поддержки.

С другой стороны, итератор, используемый вашим хеш-набором, имеет свой hasNext, реализованный следующим образом:

public final boolean hasNext() {
    return next != null;
}

Эта реализация не должна быть "уязвимой" к изменениям, внесенным в хэш-набор после завершения итерации, и как таковой лучше вести себя метод hasNext.

Ответ 3

Начните с чтения JavaDoc для Iterator. Описывает ли он ConcurrentModificationException где-нибудь?

Теперь прочитайте JavaDoc для ConcurrentModificationException и обратите внимание на следующее (выделено мной):

Это исключение может быть брошено методами, которые обнаружили одновременную модификацию объекта , если такая модификация недопустима.

Теперь внимательно посмотрите на свой код. Цикл while выполняет итерацию по всем элементам коллекции (даже если вывод вашего первого примера не указывает на это, что говорит мне, что либо вы отредактировали результат, либо это не ваш фактический код). В то время, когда вы удаляете элемент, больше нет элементов для итерации, поэтому второй цикл должен всегда выходить немедленно.

Итак, вывод заключается в том, что разработчики итератора списка выбрали это, чтобы выкинуть это исключение, даже если больше нет элементов для итерации, в то время как исполнители итератора, заданного набором, не выбрали. Оба случая полностью приемлемы с учетом спецификаций.

Ответ 4

 public static void main(String[] ar) {
            List<String> list = new ArrayList<String>();
            list.add("core java");
            list.add("php");
            list.add("j2ee");
            list.add("struts");
            list.add("hibernate");

            Iterator<String> itr = list.iterator();

            while (itr.hasNext()) {
                System.out.println(itr.next());
            }
            list.remove("php");

          /*  while (itr.hasNext()) {
                System.out.println(itr.next());
            }*/

        }

problem in itr object.it holds the list object reference

Ответ 5

Hashset может вызывать исключение ConcurrentModificationException, если вы делаете что-либо в комплекте, кроме как через итератор. Тем не менее, существует много эвристик вокруг поведения с быстрым отказом itertor с целью завершения итерации, если это вообще возможно. JavaDocs кажется довольно ясным в этом поведении.

Ответ 6

В случае списка, когда мы пересекаем его с помощью первого цикла  Итератор itr = set.iterator();

    while (itr.hasNext()) {
        System.out.println(itr.next());
    }

Значение и размер курсора станут такими же. Курсор содержит значение для общего количества пройденных элементов и внутри метода hashNext() для обхода списка содержит код как:

  public boolean hasNext() {
            return cursor != size;
        }

Итак, сначала, когда цикл loop == size.But после удаления элемента из размера списка становится (originalSize-1). Так что для следующего цикла while он внутри внутри и внутри метода itr.next() он проверяет модификацию modcount и выбрасывает ConcurrentModificationException.

В случае Set он проверяет следующее: = null для каждого вызова itr.hasnext(). И после первого прохождения, когда цикл следующий становится null.Removing element из set не влияет на следующее значение как null и itr.hasNext вернется next == null как истинный и, следовательно, он не входит внутрь цикла, чтобы проверить модификацию modcount. И, следовательно, он не бросает ConcurrentModification Exception.