Почему я не получаю 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

Вот почему: Как говорится в Джавадоке:

Итераторы, возвращаемые этим классом, итераторы и методы listIterator работают с ошибкой: если список структурно изменен в любое время после создания итератора, любым способом, кроме как через собственные методы удаления или добавления итератора, итератор будет вызывать исключение ConcurrentModificationException.

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

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

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

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

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

Резюме

List.remove() не будет 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, используйте вместо этого обычный цикл, но шаг за ним.

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.

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

JavaDocs:

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

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

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

Ответ 5

У меня была та же проблема, но в случае, если я добавлял элемент en в итерированный список. Я сделал это так.

public static void remove(Integer remove) {
    for(int i=0; i<integerList.size(); i++) {
        //here is maybe fine to deal with integerList.get(i)==null
        if(integerList.get(i).equals(remove)) {                
            integerList.remove(i);
        }
    }
}

Теперь все идет хорошо, потому что вы не создаете итератор над своим списком, вы перебираете его "вручную". И условие i < integerList.size() никогда не обманет вас, потому что когда вы удаляете/добавляете что-то в размер списка уменьшения/приращения списка.

Надеюсь, это поможет, для меня это было решением.

Ответ 6

Это отлично работает на Java 1.6

~% javac RemoveListElementDemo.java
~% java RemoveListElementDemo
~% cat RemoveListElementDemo.java

import java.util.*;
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 remove) {
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }

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

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}

~%

Ответ 7

Если вы используете коллекции copy-on-write, это сработает; однако, когда вы используете list.iterator(), возвращаемый Iterator всегда будет ссылаться на коллекцию элементов, как это было, когда (как показано ниже) list.iterator() был вызван, даже если другой поток изменяет коллекцию. Любые мутирующие методы, называемые Iterator на основе копий на запись или ListIterator (например, добавление, установка или удаление) вызовет исключение UnsupportedOperationException.

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

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

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

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

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

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}

Ответ 8

Измените Iterator for each в for loop для решения.

И причина:

Итераторы, возвращаемые этим классом, итераторы и методы listIterator работают с ошибкой: если список структурно изменен в любое время после создания итератора, любым способом, кроме как через собственные методы удаления или добавления итератора, итератор будет вызывать исключение ConcurrentModificationException.

--Referred Документы Java.

Ответ 9

В моем случае я сделал это следующим образом:

int cursor = 0;
do {
    if (integer.equals(remove))
        integerList.remove(cursor);
    else cursor++;
} while (cursor != integerList.size());

Ответ 10

Проверьте свой код человека....

В основном методе вы пытаетесь удалить 4-й элемент, который не существует и, следовательно, ошибка. В методе remove() вы пытаетесь удалить третий элемент, который есть и, следовательно, нет ошибки.