有没有办法选择 "unspecified behavior" 而不是 ConcurrentModificationException?
Is there a way to opt for "unspecified behavior" rather than ConcurrentModificationException?
我知道这样的代码
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));
}
}
其中 other(o) 在 set 的排序中保证是 "far" from o。在我的特定实现中,它与 o 的距离永远不会小于 47 "steps"。此外,如果 condition(o) 为真,则所讨论的循环将保证在到达 other(o) 所在的位置之前就已短路。因此,迭代器访问的集合的整个部分与被修改的部分完全分离。此外,LinkedHashSet 的特殊优势(快速随机访问插入和删除,保证迭代顺序)似乎特别适合这种确切的操作。
我想我的问题是双重的:首先,鉴于上述限制,这样的操作仍然危险吗?我认为可能的唯一方法是 Iterator 值提前预加载并缓存,我认为这会提高许多应用程序的性能,但似乎它也会降低许多其他应用程序的性能,因此是一个来自 java.util 的通用 class 的奇怪选择。但也许我错了。当涉及到缓存之类的事情时,我对效率的直觉常常令人怀疑。其次,假设这种事情至少在理论上是安全的,那么除了完全重新实现 LinkedHashSet 或牺牲效率之外,是否有一种方法可以实现这种操作?我可以告诉 Collections 忽略我正在修改 Set 的不同部分这一事实,并照常进行它的业务吗?我目前的解决方法是先将元素添加到中间集合,然后在循环完成后将它们添加到主集合,但这是低效的,因为它必须添加两次值。
抛出 ConcurrentModificationException
是因为您的集合可能无法始终处理删除(或添加)。例如,如果您执行的删除意味着您的 LinkedHashSet
必须 reduce/increase space 底层 HashMap
隐藏在幕后怎么办?它必须进行大量更改,这可能会使迭代器变得无用。
你有两个选择:
使用Iterator
迭代元素并删除它们,例如调用 Iterator iter = linkedHashSet.iterator()
获取迭代器,然后通过 iter.remove()
删除元素
使用 java.util.concurrent
包下可用的并发集合之一,旨在允许并发修改
This question 包含有关使用 Iterator
的详细信息
更新 评论后:
您可以使用以下模式来删除您希望的元素而不会导致 ConcurrentModificationException
:在 List
中收集您希望删除的元素,同时循环遍历 LinkedHashSet
元素。之后,遍历列表中的每个 toBeDeleted 元素并将其从 LinkedHashSet
.
中删除
我知道这样的代码
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));
}
}
其中 other(o) 在 set 的排序中保证是 "far" from o。在我的特定实现中,它与 o 的距离永远不会小于 47 "steps"。此外,如果 condition(o) 为真,则所讨论的循环将保证在到达 other(o) 所在的位置之前就已短路。因此,迭代器访问的集合的整个部分与被修改的部分完全分离。此外,LinkedHashSet 的特殊优势(快速随机访问插入和删除,保证迭代顺序)似乎特别适合这种确切的操作。
我想我的问题是双重的:首先,鉴于上述限制,这样的操作仍然危险吗?我认为可能的唯一方法是 Iterator 值提前预加载并缓存,我认为这会提高许多应用程序的性能,但似乎它也会降低许多其他应用程序的性能,因此是一个来自 java.util 的通用 class 的奇怪选择。但也许我错了。当涉及到缓存之类的事情时,我对效率的直觉常常令人怀疑。其次,假设这种事情至少在理论上是安全的,那么除了完全重新实现 LinkedHashSet 或牺牲效率之外,是否有一种方法可以实现这种操作?我可以告诉 Collections 忽略我正在修改 Set 的不同部分这一事实,并照常进行它的业务吗?我目前的解决方法是先将元素添加到中间集合,然后在循环完成后将它们添加到主集合,但这是低效的,因为它必须添加两次值。
抛出 ConcurrentModificationException
是因为您的集合可能无法始终处理删除(或添加)。例如,如果您执行的删除意味着您的 LinkedHashSet
必须 reduce/increase space 底层 HashMap
隐藏在幕后怎么办?它必须进行大量更改,这可能会使迭代器变得无用。
你有两个选择:
使用Iterator
迭代元素并删除它们,例如调用 Iterator iter = linkedHashSet.iterator()
获取迭代器,然后通过 iter.remove()
使用 java.util.concurrent
包下可用的并发集合之一,旨在允许并发修改
This question 包含有关使用 Iterator
更新 评论后:
您可以使用以下模式来删除您希望的元素而不会导致 ConcurrentModificationException
:在 List
中收集您希望删除的元素,同时循环遍历 LinkedHashSet
元素。之后,遍历列表中的每个 toBeDeleted 元素并将其从 LinkedHashSet
.