[英]Java sibling removal throws ConcurrentModificationException
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. linkedElements的所有元素也属于元素。 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.
我尝试将观察者附加到Iterator.remove操作上,并在该位置触发从元素列表中删除linkedElements,但是由于逻辑本身,我总是遇到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. 使用此代码,仅调用
A.getElements().clear()
将触发ConcurrentModificationException
...及其确定,因为我要从元素列表中删除所有链接的元素,同时删除一个元素。 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:
从ArrayList的javadocs中:
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.
此类的迭代器和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时间以任何方式对列表进行结构修改,除非通过迭代器自己的remove或add方法,否则迭代器将抛出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);
因此,一旦执行
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. 在remove方法,你现在只是通过结构比其他迭代的手段修改列表
it
的‘删除’的方法,这意味着迭代器都将抛出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. CopyOnWriteArrayList
,因为其迭代器是故障安全的。 Downside is that your iterator may potentially still show items that were removed previously. remove
, replace it
with a new Iterator that you manually advance to the correct location. remove
的for循环之后,将it
替换为新的Iterator,您可以手动将其迭代到正确的位置。 Hard to do if your list allows duplicate items. remove
method, you can track the changes you're making and update your iterator appropriately. remove
方法中,您可以跟踪所做的更改并适当地更新迭代器。 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.
现在,如果您将元素Foo链接到Bar,并将Bar链接到Baz,则从迭代器中删除Foo将导致删除Bar,但是没有什么可以删除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
. (但是,如果先删除Bar,则将Baz删除。)通常,如果要修改
Collection
的删除行为,最好将其设置为List.remove
而不是Iterator.remove
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.