Безопасно ли добавлять элементы в связанный список во время итерации?

Насколько безопасно добавлять элементы в LinkedList во время итерации?

class Worker {

    final LinkedList<Foo> worklist = new LinkedList<>();

    public void work() {

        Iterator<Foo> iterator = worklist.iterator();

        while (iterator.hasNext()) {

            Foo foo = iterator.next();

            doSomethingWith(foo);
        }
    }

    public void doSomethingWith(Foo foo) {

        // do something with foo            

        // and possibly add one (or more) foo's to the worklist
        if (expression) {
            worklist.add(new Foo());
        }
    }
}

Если нет, как можно безопасно и эффективно реализовать такое поведение?

Обратите внимание, что это не о List, но конкретно о LinkedList. Если это небезопасно, я спрашиваю об альтернативах.


person Tim    schedule 06.10.2015    source источник
comment
stackoverflow.com/questions/3362018/ Возможность изменять значения в списке во время итерации также является свойством, связанным с многопоточностью. Общее правило: если список может использовать итератор, он не является потокобезопасным   -  person Ya Wang    schedule 06.10.2015
comment
Если вы измените список во время итерации по нему, он выдаст исключение ConcurrentModificationException.   -  person YoungHobbit    schedule 06.10.2015
comment
Почему бы не использовать ListIterator и добавить элемент с помощью итератора? Мы можем получить к нему доступ с помощью worklist.listIterator ()   -  person Ivan Bochko    schedule 06.10.2015
comment
@IvanBochko Мне тоже нужно перебирать добавленные элементы.   -  person Tim    schedule 06.10.2015
comment
Возможный дубликат Java: добавление элементов в коллекцию во время итерации   -  person pelumi    schedule 06.10.2015


Ответы (1)


Нет, это небезопасно. Следующий код выдаст ConcurrentModificationException:

final LinkedList<Foo> worklist = new LinkedList<>();
worklist.add(new Foo());
Iterator<Foo> iterator = worklist.iterator();
while (iterator.hasNext()) {
    Foo foo = iterator.next();
    worklist.add(new Foo());
}

LinkedList не отменяет _ 4_ и реализация по умолчанию, определенная в _ 5_ означает вызов listIterator(), а LinkedList переопределяет _ 8_.

Цитата из документации LinkedList.listIterator:

Итератор-список работает без сбоев: если список структурно изменен в любое время после создания итератора, любым способом, кроме собственных remove или add методов итератора списка, то итератор-итератор выкинет ConcurrentModificationException.

Вы хотите явно использовать ListIterator вместо Iterator и используйте ListIterator.add:

final LinkedList<Foo> worklist = new LinkedList<>();
worklist.add(new Foo());
ListIterator<Foo> iterator = worklist.listIterator();
while (iterator.hasNext()) {
    Foo foo = iterator.next();
    iterator.add(new Foo());
}

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

person Tunaki    schedule 06.10.2015
comment
Но мне тоже нужно перебирать добавленный элемент. Я отредактировал пример кода в вопросе. - person Tim; 06.10.2015
comment
@Tim см. Мое редактирование, вы можете вызвать previous(), чтобы добавить элемент в текущую итерацию. - person Tunaki; 06.10.2015