1

我知道这样的代码

for ( Object o: collection){
    if (condition(i)){
        collection.remove(i);
    }
}

将抛出 ConcurrentModificationException,我明白为什么:直接修改集合可能会干扰迭代器跟踪其位置的能力,例如,通过引用不再是集合的一部分的元素,或者导致它跳过刚刚添加的一个。对于像上面这样的代码,这是一个合理的问题,但是,我想写一些类似的东西

for (Object o: set){// set is an instance of java.util.LinkedHashSet
    if (condition(o)){
        set.remove(other(o));
    }
}

在集合的顺序中, other(o) 保证与 o 相距“远”。在我的特定实现中,它与 o 的距离永远不会少于 47 个“步骤”。此外,如果 condition(o) 为真,则所讨论的循环将保证在到达 other(o) 所在的位置之前很好地短路。因此,迭代器访问的集合的整个部分与被修改的部分完全解耦。此外,LinkedHashSet 的特殊优势(快速随机访问插入和删除,保证迭代顺序)似乎特别适合这种精确的操作。

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

4

1 回答 1

2

ConcurrentModificationException抛出是因为您的集合可能无法始终处理删除(或添加)。例如,如果您执行的移除意味着您LinkedHashSet必须减少/增加底层HashMap在引擎盖下占用的空间怎么办?它必须进行大量更改,这可能会使迭代器无用。

你有两个选择:

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

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

这个问题包含关于使用的很好的细节Iterator

评论后更新

您可以使用以下模式来删除您希望删除的元素而不会导致:在循环遍历元素ConcurrentModificationException的同时收集您希望删除的元素。然后,循环遍历列表中的每个toBeDeleted元素并将其从.ListLinkedHashSetLinkedHashSet

于 2017-02-02T06:20:03.800 回答