2011-02-01 127 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); 
    } 
相關問題