ConcurrentModificationException при добавлении внутри цикла foreach в ArrayList
Я пытаюсь использовать цикл foreach с arraylist, но когда я его использую, он дает мне ошибку, но когда я использую обычный цикл, он отлично работает, что может быть проблемой?
Код находится здесь:
for (Pair p2 : R) {
if ((p2.getFirstElm() == p.getSecondElm()) && (p2.getFirstElm() != p2.getSecondElm()))
R.add(new Pair (p.getFirstElm(), p2.getSecondElm()));
else if ((p2.getSecondElm() == p.getFirstElm()) && (p2.getFirstElm() != p2.getSecondElm()))
R.add(new Pair (p2.getFirstElm(), p.getSecondElm()));
// else
// There are no transitive pairs in R.
}
это цикл, который не работает, и вот тот, который работает:
for (int i = 0; i < R.size(); i++) {
if ((R.get(i).getFirstElm() == p.getSecondElm()) && (R.get(i).getFirstElm() != R.get(i).getSecondElm()))
R.add(new Pair (p.getFirstElm(), R.get(i).getSecondElm()));
else if ((R.get(i).getSecondElm() == p.getFirstElm()) && (R.get(i).getFirstElm() != R.get(i).getSecondElm()))
R.add(new Pair (R.get(i).getFirstElm(), p.getSecondElm()));
//else
// There are no transitive pairs in R.
}
ошибка, которую я получаю при использовании цикла foreach:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)
at set.problem.fourth.PoSet.makeTransitive(PoSet.java:145)
at set.problem.fourth.PoSet.addToR(PoSet.java:87)
at set.problem.fourth.PoSetDriver.typicalTesting(PoSetDriver.java:35)
at set.problem.fourth.PoSetDriver.main(PoSetDriver.java:13)
Ответы
Ответ 1
Классы Java Collection неудачны, что означает, что если Коллекция будет изменена, пока какой-то поток пересекает ее используя итератор, iterator.next()
будет бросать ConcurrentModificationException
.
Эта ситуация может возникнуть в случае многопоточных, а также одиночных с резьбой. - www.javacodegeeks.com
Вы не можете изменить List
в цикле for/each
, который является синтаксическим сахаром вокруг Iterator
в качестве детали реализации. Вы можете безопасно звонить .remove()
только при непосредственном использовании Iterator
.
Обратите внимание, что Iterator.remove является единственным безопасным способом изменения коллекции во время итерации; поведение неуказано, если основной коллекция модифицируется любым другим способом, пока итерация прогресс. - Коллекции Java Учебник
Вызов .add()
внутри цикла for/each
изменяет содержимое, а Iterator
, который используется за кулисами, видит это и выдает это исключение.
Более тонкая проблема заключается в том, что второй способ, который вы перечисляете, .size()
увеличивается каждый раз, когда вы .add()
, так что вы закончите обработку всех вещей, которые вы .add()
, это может привести к бесконечному циклу на каких входных данных. Я не уверен, что это то, чего вы желаете.
Решение
Я бы создал еще ArrayList
и .add()
для всех новых вещей, а затем после цикла использовал .addAll()
в исходном ArrayList
, чтобы объединить два списка вместе. Это сделает вещи явными в том, что вы пытаетесь сделать, то есть, если ваше намерение не обрабатывает все добавленные вами вещи, когда вы их добавляете.
2014 Решение:
Всегда используйте классы коллекций Immutable
и создавайте новые классы коллекций Immutable
вместо того, чтобы пытаться изменить один общий. Это в основном то, что говорит мой ответ 2012 года, но я хотел сделать его более явным.
Guava поддерживает это очень хорошо, используйте ImmutableList.copyOf()
для передачи данных.
Использовать Iterables.filter()
, чтобы отфильтровать материал в новом ImmutableList
, без общего измененного состояния, означает отсутствие проблем concurrency!
Ответ 2
Под капотом для каждого цикла в Java используется Iterator
для перемещения коллекции (см. статью для подробного объяснение.) И Итератор будет бросать ConcurrentModificationException
, если вы измените коллекцию, итерации по ней, см. этот пост.
Ответ 3
Проблема в том, что вы делаете R.add() в первой строке цикла.
В первой ситуации у вас есть итератор, открытый для arraylist. Когда вы делаете добавление, а затем пытаетесь повторить итерацию, итератор замечает, что структура данных изменилась под вами.
В случае поиска for вы просто получаете новый элемент каждый раз и не имеете проблемы с одновременной модификацией, хотя размер изменяется при добавлении дополнительных элементов.
Чтобы устранить проблему, которую вы, вероятно, захотите добавить во временное местоположение, добавьте ее после цикла или сделайте копию исходных данных и добавьте в оригинал.