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

Безопасно ли добавлять элементы в 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, Если это не безопасно, я спрашиваю об альтернативах.

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 не переопределяет iterator() и реализация по умолчанию, определенная в AbstractSequentialList это позвонить listIterator(), а также LinkedList переопределяет listIterator,

Цитирование документации 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() (и игнорировать возвращаемое значение) после добавления элемента для перемещения курсора назад.

Другие вопросы по тегам