I'm having the following issue:
Given:
public class A{
Collection<B> elements = new ArrayList<B>();
}
public class B{
Collection<B> linkedElements = new ArrayList<B>();
}
All the elements of linkedElements belongs to elements too. I want that each time that an element is deleted from the elements collection, its linked elements gets deleted from that collection too. I've tried attaching a observer to the Iterator.remove operation and fire there the removal of linkedElements from the elements list, but because of the logic itself, I always run into a ConcurrentModificationException.
Update: This is the code that causes the error:
public class A{
Collection<B> elements = new ArrayList<B>(){
public Iterator<B> iterator() {
return new ProxyIterator(super.iterator());
};
private class ProxyIterator implements Iterator{
Iterator it;
Object lastObject;
public ProxyIterator(Iterator proxied){
it = proxied;
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Object next() {
return lastObject = it.next();
}
@Override
public void remove() {
it.remove()
for (B linkedElement : ((B)lastObject).getlinkedElements()) {
A.this.getElements().remove(linkedElement);
}
}
}
}
With this code, just calling a A.getElements().clear()
will fire a ConcurrentModificationException
... and its ok, because I'm removing all linked elements from from the elements list while removing one single element. That's why I need another approach.
It's because you're modifying the array while you're iterating over it. From the javadocs for ArrayList:
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
So, once you do A.this.getElements().remove(linkedElement);
in the remove method, you've now just structurally modified the list via a means other than the iterator it
's "remove" method, which means the iterator will throw the CME.
Dealing with this will probably be tricky. I can think of a few options offhand, all of which have complications:
CopyOnWriteArrayList
, since its iterators are fail-safe. Downside is that your iterator may potentially still show items that were removed previously. (On the other hand, you have to deal with that sort of risk anyway, since you could also have already iterated past a child of whatever you're removing.) If this works for you, this is almost certainly the easiest and most reliable option.remove
, replace it
with a new Iterator that you manually advance to the correct location. Hard to do if your list allows duplicate items.remove
method, you can track the changes you're making and update your iterator appropriately.Finally, as a last question / warning - do you want this traversal to be recursive? Right now, if you had elements Foo linked to Bar, and Bar linked to Baz, removing Foo from the iterator would result in Bar being removed, but there's nothing that would then remove Baz. (Whereas if you removed Bar first, then Baz would be removed.) In general if you want to modify the removal behavior of a Collection
, you'd be better off doing it List.remove
rather than Iterator.remove
.