这个操作安全吗?

时间:2011-02-01 17:22:26

标签: java iterator containers

此操作安全吗? 如果没有,为了做同样的事情,如何编写代码??

Set<Object> set;

......

for (Object o: set) {
    if (some_condition) {
        set.remove(o);
    }
}

4 个答案:

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