2011-02-01 84 views
2

此操作是否安全?如果不是,为了做同样的事情,如何编写代码?此操作是否安全?

Set<Object> set; 

...... 

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

回答

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); 
+0

第一句:*因为你不是很多*? – 2011-02-01 17:33:59

1

现在,它会抛出ConcurrentModificationException

for()循环使用的Iterator内部,保持一个编辑计数和如果编辑计数不匹配的后盾Set的编辑数量,它抛出一个ConcurrentModificationException

当然,这取决于实际执行收集,但这是记录的行为,例如在the HashSet docs

此类的 iterator方法返回的迭代器是快速失败的:如果 组随时修改后的 迭代器是通过迭代器自身的移除 创建的,以任何方式,除了 方法,Iterator将抛出一个 ConcurrentModificationException。因此, 并发 修改的,迭代器很快就会完全失败 ,而不是 在 未来不确定的时间冒着任意的,非确定性 行为。

2

否 - 您的条件首次通过时,您会在下一次迭代中获得ConcurrentModificationException

在一般情况下,它是不是安全的直接修改集合在遍历它,和迭代器是在这种情况下,“快速失败”(因为整体上,而他们可以检测到的变化,有没有通用方式弄清楚如何应对他们)。

在这种情况下,一个工作的习惯用法是使用迭代器自己的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,所以没有发生并发修改,并且迭代器很满意。

+1

这就是“总是工作“,只要设置支持删除一般,当然:) – 2011-02-01 17:31:09

+0

很好的解释! – peakit 2011-02-01 17:37:35

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); 
    } 
相关问题