Вопрос-ответ

Why am I not getting a java.util.ConcurrentModificationException in this example?

Почему я не получаю исключение java.util.ConcurrentModificationException в этом примере?

Примечание: Я знаю о Iterator#remove() методе.

В следующем примере кода я не понимаю, почему метод List.remove in main выдает ошибку ConcurrentModificationException, но не в методе remove.

public class RemoveListElementDemo {    
private static final List<Integer> integerList;

static {
integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
}

public static void remove(Integer toRemove) {
for(Integer integer : integerList) {
if(integer.equals(toRemove)) {
integerList.remove(integer);
}
}
}

public static void main(String... args) {
remove(Integer.valueOf(2));

Integer toRemove = Integer.valueOf(3);
for(Integer integer : integerList) {
if(integer.equals(toRemove)) {
integerList.remove(integer);
}
}
}
}
Переведено автоматически
Ответ 1

Вот почему: Как сказано в Javadoc:


Итераторы, возвращаемые методами iterator и ListIterator этого класса, быстродействующие: если список структурно изменен в любое время после создания итератора любым способом, кроме как с помощью собственных методов remove или add итератора, итератор выдаст исключение ConcurrentModificationException .


Эта проверка выполняется в next() методе итератора (как вы можете видеть по stacktrace). Но мы достигнем next() метода только в том случае, если hasNext() выдано значение true , которое вызывается for each, чтобы проверить, соблюдена ли граница. В вашем методе remove, когда hasNext() проверяет, нужно ли ему возвращать другой элемент, он увидит, что он вернул два элемента, и теперь, после удаления одного элемента, список содержит только два элемента. Итак, все отлично, и мы закончили с итерацией. Проверка одновременных изменений не происходит, поскольку это выполняется в next() методе, который никогда не вызывается.

Далее мы переходим ко второму циклу. После удаления второго числа метод hasNext снова проверит, может ли вернуть больше значений. Он уже вернул два значения, но список теперь содержит только одно. Но приведенный здесь код:

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

1 != 2, поэтому мы переходим к next() методу, который теперь понимает, что кто-то возился со списком, и запускает исключение.

Надеюсь, это прояснит ваш вопрос.

Краткие сведения

List.remove() не будет выдавать ошибку ConcurrentModificationException при удалении предпоследнего элемента из списка.

Ответ 2

Один из способов справиться с этим - удалить что-либо из копии Collection (не саму коллекцию), если применимо. Clone исходную коллекцию нужно скопировать через Constructor.


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


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

private static final List<Integer> integerList;

Также рассмотрите возможность изменения копии вместо исходного списка.

List<Integer> copy = new ArrayList<Integer>(integerList);

for(Integer integer : integerList) {
if(integer.equals(remove)) {
copy.remove(integer);
}
}
Ответ 3

Метод forward / iterator не работает при удалении элементов. Вы можете удалить элемент без ошибок, но вы получите ошибку времени выполнения при попытке получить доступ к удаленным элементам. Вы не можете использовать итератор, потому что, как показывает pushy, это вызовет исключение ConcurrentModificationException , поэтому вместо этого используйте обычный цикл for , но выполните его в обратном порядке.

List<Integer> integerList;
integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);

int size= integerList.size();

//Item to remove
Integer remove = Integer.valueOf(3);

Решение:

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

//To remove items from the list, start from the end and go backwards through the arrayList
//This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error
//for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator
for (int i=size-1; i> -1; i--) {
if (integerList.get(i).equals(remove) ) {
integerList.remove(i);
}
}
Ответ 4

Этот фрагмент всегда будет вызывать исключение ConcurrentModificationException.

Правило гласит: "Вы не имеете права изменять (добавлять или удалять элементы из списка) при выполнении итерации по нему с помощью итератора (что происходит, когда вы используете цикл for-each)".

JavaDocs:

Итераторы, возвращаемые методами iterator и ListIterator этого класса, быстродействующие: если список структурно изменен в любое время после создания итератора любым способом, кроме как с помощью собственных методов remove или add итератора, итератор выдаст исключение ConcurrentModificationException .

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

Надеюсь, это поможет.

java list