Итератор внутри Iterator ConcurrentModificationException

У меня следующая проблема:

Имея ArrayList (давайте назовем его списком), как я могу «двойную итерацию» пройти через него, не получая ConcurrentModificationException?

Вот что я пробовал:

iterator out = list.iterator();
iterator in;
while(out.hasNext()){
    ...
    in = list.iterator();
    while(in.hasNext()){
        ...
        if(something)
             in.remove();
    }

person ivanciprian    schedule 09.06.2017    source источник
comment
что это?????   -  person Maurice Perry    schedule 09.06.2017
comment
это в, извините, я набрал в спешке   -  person ivanciprian    schedule 09.06.2017
comment
Вы не можете использовать два итератора одного и того же списка и удалить элемент с помощью одного итератора, в то время как другой итератор все еще занят перебором этой коллекции.   -  person Luciano van der Veekens    schedule 09.06.2017
comment
А зачем тебе это? Не могли бы вы объяснить, чего вы хотите достичь с помощью этого подхода с двойной итерацией?   -  person tmarwen    schedule 09.06.2017


Ответы (4)


Вы не можете этого сделать. Возможным решением может быть пометка объектов для удаления, например:

final List<Foo> toRemove = new ArrayList<>();

for (Foo a : list)
{
    for (Foo b : list)
    {
        if (something)
        {
            toRemove.add(b);
        }
    }
}
list.removeAll(toRemove);

Вам могут потребоваться дополнительные проверки, чтобы убедиться, что объект еще не помечен для удаления. Невозможно сказать, учитывая, насколько расплывчат ваш пример.

person Michael    schedule 09.06.2017

Вы пытаетесь изменить итератор. Это даст вам исключение concurrentModification.

В java 8 вы можете легко удалить его, используя list.removeIf(someCondition)

Попробуйте эту ссылку коллекции java8

person Binu    schedule 09.06.2017

Экземпляр Iterator, предоставляемый посредством вызова метода List#iterator, сохраняет скаляр count, позволяющий обнаруживать внешние изменения в контейнере Collection.

Когда элемент удаляется из коллекции каким-либо другим способом, кроме выполнения того же вызова Iterator#remove(T), счетчик не обновляется за кулисами. Поэтому, когда вы запрашиваете элемент #next() через экземпляр итератора, счетчик сравнивается с ожидаемым значением, и если оба значения не совпадают (поскольку элемент был удален через другой итератор) выдается ConcurrentModificationException (даже если вы работаете в среде с одним потоком).

Как заявил @Michael, решение должно состоять в том, чтобы отслеживать элементы контейнера, которые следует удалить, а затем выполнять массовое удаление:

Collection<Object> temp = new ArrayList<>();
iterator out = list.iterator();
iterator in;
while (out.hasNext()) {
    // ...
    in = list.iterator();
    while (in.hasNext()) {
        // ...
        if(something)
             // just mark the element for deletion
             temp.add(in.next());
    }
}

// delete all the obsolete elements
list.removeAll(temp);
person tmarwen    schedule 09.06.2017

В коллекции один раз создатель итератора. Если вы попытаетесь изменить содержимое не через тот же итератор, он вызовет параллельное исключение. Если вам нужен какой-то особый тип итератора, вы можете реализовать свой собственный.

person gati sahu    schedule 09.06.2017
comment
В этом случае он вызовет исключение в основном потоке java.lang.IllegalStateException, а не в исключении одновременной модификации. Я ищу причину этого. - person Pradeep Singh; 09.06.2017