Java: невозможно изменить список итераторов

63
7

У меня есть список ListIterator<PointF> как поле класса. Я заполняю его методом grow(). Когда я пытаюсь использовать итераторы из этого списка, я получаю ConcurrentModificationException.


ListIterator<ListIterator<PointF>> i = mPoints.listIterator();
while (i.hasNext()) {
ListIterator<PointF> j = i.next();
if (j.hasNext())
PointF tmp = j.next(); // Exception here
}

Я понятия не имею, почему этот код вызывает exeption в любом методе кроме grow()

спросил(а) 2021-01-25T14:46:12+03:00 4 месяца, 4 недели назад
1
Решение
98

Если базовый список изменяется, то итератор, который был получен до этого, выбрасывает ConcurrentModificationException. Поэтому не сохраняйте итераторы в полях экземпляров.

ответил(а) 2021-01-25T14:46:12+03:00 4 месяца, 4 недели назад
45

Мы можем с уверенностью сказать, что ConcurrentModificationException означает, что базовый итерабель был изменен в какой-то момент после вашего вызова, чтобы получить итератор.


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


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

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


ListIterator<Iterable<PointF>> i = mPoints.listIterator();
while (i.hasNext()) {
Iterator<PointF> j = i.next().iterator();
if (j.hasNext())
PointF tmp = j.next();
}

Затем точное решение зависит от общей архитектуры вашего метода. Главное, чтобы иметь в виду не хранить итераторы долгосрочные, потому что почти невозможно сделать эту работу надежно. Даже если он работает прямо сейчас, он создает своего рода невидимую зависимость между различными частями вашего кода, которые почти всегда будут нарушены кем-то, реализующим то, что должно быть тривиальным изменением.

ответил(а) 2021-01-25T14:46:12+03:00 4 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема