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.
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.
Because you iterate over a copy of the list, you can modify the original list without damaging the iterator.
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.
The size of the List is not being changed, but the object at the index is changing, so technically the List is being modified.
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
oradd
methods, the list-iterator will throw aConcurrentModificationException
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With