我知道这样的代码

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));
    }
}


在保证set的顺序中other(o)距离o远的地方。在我的特定实现中,它离o不会少于47个“步”。此外,如果condition(o)为true,则可以保证所讨论的环路在到达other(o)的位置之前就已经短路了。因此,迭代器访问的集合的整个部分与修改的部分完全脱钩。此外,LinkedHashSet的特殊优势(快速的随机访问插入和移除,保证迭代顺序)似乎特别适合这种精确的操作。

我想我的问题是双重的:首先,鉴于上述限制,这样的操作是否仍然很危险?我认为可能的唯一方法是将Iterator值预先预先加载并缓存,我想这会提高许多应用程序的性能,但似乎也会降低许多其他应用程序的性能,因此是java.util中通用类的奇怪选择。但也许我错了。当涉及到诸如缓存之类的事情时,我对效率的直觉常常令人怀疑。其次,至少从理论上讲,假设这种事情是安全的,是否有办法完全重新实现LinkedHashSet或牺牲效率来实现此操作?我是否可以让Collections忽略我正在修改Set的不同部分,而照常进行处理的事实?我当前的解决方法是先将元素添加到中间集合,然后在循环完成后将它们添加到主集合中,但这效率低下,因为它必须将值添加两次。

最佳答案

抛出ConcurrentModificationException的原因是您的收藏集可能无法始终处理删除(或添加)。例如,如果执行删除操作意味着您的LinkedHashSet必须减少/增加底层HashMap占用的内部空间,该怎么办?它必须进行很多更改,这可能会使迭代器无用。

您有两种选择:

使用Iterator迭代元素并删除它们,例如调用Iterator iter = linkedHashSet.iterator()获取迭代器,然后通过iter.remove()删除元素

使用java.util.concurrent包下可用的并发集合之一,这些并发集合旨在允许并发修改

This question包含有关使用Iterator的详细信息

评论后更新:

您可以使用以下模式来删除所需元素而不引起ConcurrentModificationException:在遍历List元素的同时,将要删除的元素收集在LinkedHashSet中。然后,遍历列表中的每个toBeDeleted元素,并将其从LinkedHashSet中删除​​。

关于java - 有没有办法选择“未指定行为”而不是ConcurrentModificationException?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41995278/

10-10 02:43