此操作安全吗? 如果没有,为了做同样的事情,如何编写代码??
Set<Object> set;
......
for (Object o: set) {
if (some_condition) {
set.remove(o);
}
}
答案 0 :(得分:8)
不,它不是 - 因为除了通过迭代器之外,你不允许修改你正在迭代的集合。有各种方法可以解决这个问题。这是一个:
for (Iterator<Object> iterator = set.iterator(); iterator.hasNext(); )
{
Object value = iterator.next();
if (someCondition)
{
iterator.remove();
}
}
另一种选择是构建一个单独的要删除的项目列表,然后在遍历集合后删除所有
List<Object> itemsToRemove = new ArrayList<Object>();
for (Object x in set)
{
if (someCondition)
{
itemsToRemove.add(x);
}
}
set.removeAll(itemsToRemove);
答案 1 :(得分:2)
不 - 你的病情第一次过去,你会在下一次迭代时被ConcurrentModificationException
抛出。
一般来说,在迭代时直接修改集合是不安全的,并且在这种情况下迭代器是“快速失败”的(因为总的来说它们可以检测到变化,所以没有一般的方法来解决如何应对它们。)
在这种情况下,一个有效的习语就是使用Iterator自己的remove()
方法。以这种受控方式删除元素可以使迭代器了解正在发生的事情,并且对于迭代顺序应该发生的事情具有一致的语义,因此可以正常工作:
Iterator<Object> iter = set.iterator();
while (iter.hasNext()) {
final Object o = iter.next();
if (some_condition) {
iter.remove();
}
}
但请注意,remove()
是“可选操作”,并非所有集合的迭代器都支持它。如果您遇到这种情况,另一种始终可行的方法是获取该集的副本,遍历 并从原始集中删除对象,像这样:
Set<Object> copy = new HashSet<Object>(set);
for (Object o: copy) {
if (some_condition) {
set.remove(o);
}
}
因为迭代超过copy
,而修改发生在set
,所以不会发生并发修改并且迭代器很满意。
答案 2 :(得分:1)
现在,它会抛出一个ConcurrentModificationException
。
for()
循环在内部使用Iterator
来保持编辑计数,如果该编辑计数与后备Set
的编辑计数不匹配,则会抛出{{1} }}
当然这取决于集合的实际实现,但这是记录的行为,例如在the HashSet
docs中:
此类返回的迭代器 迭代器方法是快速失败的:如果是 set之后的任何时候都会被修改 除了以外的任何方式创建迭代器 通过迭代器自己删除 方法,
ConcurrentModificationException
抛出一个Iterator
。从而, 面对并发 修改,迭代器失败 快速而干净,而不是 冒着任意,不确定的风险 在一个不确定的时间的行为 未来。
答案 3 :(得分:0)
Set<Object> set;
......
Set<Object> objectsToBeDeleted = new HashSet<Object>();
for (Object o : set) {
if (some_condition) {
objectsToBeDeleted.add(o);
}
}
for (Object o : objectsToBeDeleted) {
set.remove(o);
}