Is it safe to add items to a linked list while iterating
Asked Answered
L

1

7

Is it safe to add items to a LinkedList while iterating?

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());
        }
    }
}

If not, how can this behaviour be implemented in a safe and efficient way?

Note that this is not about a List, but specifically about a LinkedList. If it isn't safe, I'm asking about alternatives.

Largehearted answered 6/10, 2015 at 12:44 Comment(5)
#3362518 Being able to change values within a list while iterating is also a property associated with multithreading. General rule if a list can use a iterator it isn't thread safeDecane
If you change the list while iterating over it, it will throw ConcurrentModificationException.Seabolt
Why not to use ListIterator and add element with help of iterator? We can access it with worklist.listIterator()Zenobiazeolite
@IvanBochko I have to iterate over the added elements too.Largehearted
Possible duplicate of Java: adding elements to a collection during iterationSuave
P
12

No, it is not safe. The following code will throw a 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 does not override iterator() and the default implementation, defined in AbstractSequentialList is to call listIterator(), and LinkedList does override listIterator.

Quoting the documentation of LinkedList.listIterator:

The list-iterator is fail-fast: if the list is structurally modified at any time after the Iterator is created, in any way except through the list-iterator's own remove or add methods, the list-iterator will throw a ConcurrentModificationException.

What you want is to use explicitely a ListIterator, instead of an Iterator, and use 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());
}

The new element is inserted before the element that was returned by next() so subsequent calls to next() are unaffected. If you want to add the new item to the iteration, you can call previous() (and ignore the return value) after adding the element to move the cursor backwards.

Pestana answered 6/10, 2015 at 12:56 Comment(2)
But I have to iterate over the added element too. I've edited the code example in the question.Largehearted
@Largehearted see my edit, you can call previous() to add the element to the current iteration.Pestana

© 2022 - 2024 — McMap. All rights reserved.