我知道像
这样的代码for ( Object o: collection){
if (condition(i)){
collection.remove(i);
}
}
将抛出一个ConcurrentModificationException,我理解为什么:直接修改集合会干扰Iterator跟踪其位置的能力,例如,通过引用一个不再属于其中的元素的引用收集,或导致它跳过刚刚添加的一个。对于像上面这样的代码,这是一个合理的问题,但是,我想写一些类似
的内容for (Object o: set){// set is an instance of java.util.LinkedHashSet
if (condition(o)){
set.remove(other(o));
}
}
其他(o)在集合的顺序中保证与o“远”。在我的特定实现中,它将永远不会少于47“步”。另外,如果条件(o)为真,则保证所讨论的环路在到达其他(o)所在的位置之前短路。因此,迭代器访问的集合的整个部分与被修改的部分完全分离。此外,LinkedHashSet的特殊优势(快速随机访问插入和删除,保证迭代顺序)似乎特别适合这种精确的操作。
我想我的问题是双重的:首先,考虑到上述限制,这样的操作是否仍然很危险?我认为可能的唯一方法是Iterator值预先提前预加载并缓存,我认为这会提高许多应用程序的性能,但似乎它也会在许多其他应用程序中减少它,因此它是一个来自java.util的通用类的奇怪选择。但也许我错了。谈到缓存等问题,我对效率的直觉常常令人怀疑。其次,假设这种事情,至少在理论上是安全的,有没有办法,没有完全重新实现LinkedHashSet,或牺牲效率,来实现这个操作?我可以告诉Collections忽略我正在修改Set的不同部分的事实,并且像往常一样开展业务吗?我目前的解决方法是首先向中间集合添加元素,然后在循环完成后将它们添加到主集合中,但这样做效率很低,因为它必须将值添加两次。
答案 0 :(得分:2)
抛出ConcurrentModificationException
因为您的收藏集可能无法始终处理删除(或添加)。例如,如果您执行的移除意味着您的LinkedHashSet
必须减少/增加底层HashMap
占据的空间?它必须进行很多更改,这可能会使迭代器变得无用。
您有两种选择:
使用Iterator
来迭代元素并将其删除,例如调用Iterator iter = linkedHashSet.iterator()
来获取迭代器,然后按iter.remove()
使用java.util.concurrent
包下可用的其中一个并发集合,这些集合旨在允许并发修改
This question包含有关使用Iterator
更新:
您可以使用以下模式删除所需的元素,而不会导致ConcurrentModificationException
:在循环浏览List
元素时,在LinkedHashSet
中收集要删除的元素。然后,遍历列表中的每个 toBeDeleted 元素,并将其从LinkedHashSet
中删除。