Подтвердить что ты не робот

Java.util.ConcurrentModificationException с итератором

Я знаю, если бы попытался удалить из цикла коллекции через него с помощью простого цикла, я получаю это исключение: java.util.ConcurrentModificationException. Но я использую Iterator, и он все еще генерирует мне это исключение. Любая идея, почему и как ее решить?

HashSet<TableRecord> tableRecords = new HashSet<>();

...

    for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) {
        TableRecord record = iterator.next();
        if (record.getDependency() == null) {
            for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) {
                TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception
                if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) {
                    iterator.remove();
                }
            }
        }
    }
4b9b3361

Ответ 1

Вы должны использовать iterator.remove() вместо tableRecords.remove()

Вы можете удалить элементы в списке, на котором вы выполняете итерацию, только если вы используете метод remove из итератора.

ИЗМЕНИТЬ:

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

Это означает, что вам нужно изменить свой код, чтобы вы удаляли только элементы через iterator.remove(и только с одним итератором).

ИЛИ

создайте список элементов для удаления, затем удалите их после завершения итерации.

Ответ 2

Итератор с ошибкой проверяет свойства для любой модификации в структуру базовой коллекции каждый раз, когда мы пытаемся получить следующий элемент. Если есть какие-либо изменения, они бросают ConcurrentModificationException. Все реализации Итератора в классах коллекций отказоустойчивы по дизайну, за исключением параллельных таких как ConcurrentHashMap и CopyOnWriteArrayList.

Источник: Google

Вы лучше поймете пример, приведенный ниже: -

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class IteratorExp {
    public static void main(String... q) {
        //CASE - ONE
        List<String> strList = new ArrayList<>(Arrays.asList("a", "b", "c"));
        Iterator<String> itr = strList.iterator();
        /*
         * strList.add("e"); strList.add("f"); strList.add("g");
         */
        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
        /*
         * Exception in thread "main" java.util.ConcurrentModificationException
         * at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at
         * java.util.ArrayList$Itr.next(Unknown Source) at
         * IteratorExp.main(IteratorExp.java:14)
         */

        //CASE - TWO 
        List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0));
        Iterator<Integer> itrOne = intList.iterator();
        Iterator<Integer> itrTwo = intList.iterator();
        for (; itrOne.hasNext();) {
            if (itrOne.next().equals(5)) {
                itrOne.remove(); // #1
                //intList.remove(itrOne.next()); // #2
            }
        }
        for (; itrTwo.hasNext();) {
            if (itrTwo.next().equals(5)) {
                itrTwo.remove(); // #1
                //intList.remove(itrTwo.next()); // #2
            }
        }

        /*
         * Exception in thread "main" java.util.ConcurrentModificationException
         * at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at
         * java.util.ArrayList$Itr.next(Unknown Source) at
         * IteratorExp.main(IteratorExp.java:35)
         */
    }
}

Ответ 3

Контракт для итератора HashSet заключается в том, что вы не можете удалить из hashset, кроме как через этот метод удаления удаленных итераторов. С точки зрения dependencyIt вы удалили элемент, отличный от вызова его метода remove, чтобы он выбрал ConcurrentModificationException.

Кажется, вы хотите удалить записи из своего хэшета, если у них одинаковый идентификатор записи. Не было бы проще переопределить ваши методы записи equals и hashcode, чтобы гарантировать, что записи с одинаковым идентификатором равны и имеют один и тот же хэш-код? (если это имеет смысл, конечно)

Ответ 4

Проблема в том, что у вас одновременно есть два итератора, и они "сражаются" друг с другом. Самый простой способ исправить проблему - просто вырваться из внутреннего цикла, если вы найдете совпадение:

for (Iterator<TableRecord> iterator = tableRecords.iterator(); iterator.hasNext(); ) {
    TableRecord record = iterator.next();
    if (record.getDependency() == null) {
        for (Iterator<TableRecord> dependencyIt = tableRecords.iterator(); dependencyIt.hasNext(); ) {
            TableRecord dependency = dependencyIt.next(); //Here is the line which throws this exception
            if (dependency.getDependency() != null && dependency.getDependency().getId().equals(record.getId())) {
                iterator.remove();
                break; // ADD THIS LINE
            }
        }
    }
}

Java Iterator предназначены для "быстрого сбоя", когда их базовый контейнер изменяется без изменения с помощью Iterator. Вы используете вложенные итераторы, поэтому любая операция remove(), выдаваемая одному, заставит другого выбросить Exception, если он будет использоваться. По этой причине, если вам нужно выпустить remove(), вам нужно будет сделать это на "внешнем" итераторе (который вы делаете) и прекратите использование второго итератора впоследствии (который добавил break оператор делает).