Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to add items to a linked list while iterating

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.

like image 598
Tim Avatar asked Oct 06 '15 12:10

Tim


People also ask

Can we add elements in list while iterating?

You can't modify a Collection while iterating over it using an Iterator , except for Iterator. remove() . This will work except when the list starts iteration empty, in which case there will be no previous element. If that's a problem, you'll have to maintain a flag of some sort to indicate this edge case.

Can we modify list while iterating?

Because you iterate over a copy of the list, you can modify the original list without damaging the iterator.

Can we add elements to ArrayList while iterating?

ListIterator supports to add and remove elements in the list while we are iterating over it. listIterator. add(Element e) – The element is inserted immediately before the element that would be returned by next() or after the element that would be returned previous() method.

What happens if you modify the value of an element in a list while iterating using iterators?

The size of the List is not being changed, but the object at the index is changing, so technically the List is being modified.


1 Answers

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.

like image 180
Tunaki Avatar answered Oct 13 '22 01:10

Tunaki