2012-01-09 38 views
9

現在中刪除的項目,我能想到的最好的是:有效地從「的foreach」

bool oneMoreTime = true; 
while (oneMoreTime) 
{ 
    ItemType toDelete=null; 
    oneMoreTime=false; 
    foreach (ItemType item in collection) 
    { 
     if (ShouldBeDeleted(item)) 
     { 
      toDelete=item; 
      break; 
     } 
    } 
    if (toDelete!=null) 
    { 
     collection.Remove(toDelete); 
     oneMoreTime=true; 
    } 
} 

我知道,我至少有一個額外的變量在這裏,但我把它提高可讀性該算法。

+0

可能重複的[如何有條件地從.NET集合中刪除項目](http://stackoverflow.com/questions/653596/how-to-conditionally-remove-items-from-a-net-收集) – 2012-01-09 17:29:21

回答

31

「RemoveAll」方法是最好的。

另一種常見的方法是:

var itemsToBeDeleted = collection.Where(i=>ShouldBeDeleted(i)).ToList(); 
foreach(var itemToBeDeleted in itemsToBeDeleted) 
    collection.Remove(itemToBeDeleted); 

另一種常見的方法是使用「for」循環,但要確保你去向後

for (int i = collection.Count - 1; i >= 0; --i) 
    if (ShouldBeDeleted(collection[i])) 
     collection.RemoveAt(i); 

另一種常見的方法是添加的項目不是被刪除到一個新的集合:

var newCollection = new List<whatever>(); 
foreach(var item in collection.Where(i=>!ShouldBeDeleted(i)) 
    newCollection.Add(item); 

現在你有兩個集合。如果你想結束兩個集合,我特別喜歡的技術是使用不可變的數據結構。使用不可變的數據結構,「刪除」項目不會改變數據結構;它會給你一個新的數據結構(如果可能的話,它會重新使用舊數據結構中的數據),它沒有你刪除的項目。隨着不可變的數據結構你是不是修改你迭代的東西,所以沒有問題:

var newCollection = oldCollection; 
foreach(var item in oldCollection.Where(i=>ShouldBeDeleted(i)) 
    newCollection = newCollection.Remove(item); 

var newCollection = ImmutableCollection<whatever>.Empty; 
foreach(var item in oldCollection.Where(i=>!ShouldBeDeleted(i)) 
    newCollection = newCollection.Add(item); 

當你做,你有兩個集合。新的物品被刪除,舊的和以前一樣。

+0

你有沒有使用'反向'擴展使用foreach - 我剛剛在這裏http://stackoverflow.com/a/10541025/706363?爲什麼它不會是一個更廣泛使用的選項?是否存在某種嚴重的性能影響或正在發生的事情? – ppumkin 2014-04-29 10:12:28

+0

@ppumkin:嘗試爲不執行'IList','ICollection'等的'IEnumerable'編寫'Reverse'的實現。你實現的內存和時間表現如何? – 2014-04-29 12:55:54

+0

我的實施不是時間或資源關鍵。我在List的Linq上使用'IEnumerable.Reverse ''擴展名,它似乎在'foreach'內工作正常 - 這就是爲什麼我問,爲什麼這不是更頻繁地使用一個例子,而不是所有這些(我0)反向,而不是像你的答案。是否使用反向擴展一個有效的選項?您可以將它添加到您的答案中,或者將Linq的反向擴展與foreach結合使用時出現問題嗎?只是想,你的意見會因爲你的經驗而使運載量最重。 – ppumkin 2014-04-29 13:10:25

13

正如我完成打字我記得有lambda的方式來做到這一點。

collection.RemoveAll(i=>ShouldBeDeleted(i)); 

更好的方法?

+2

僅供參考:可以將其轉換爲方法組。 collection.RemoveAll(ShouldBeDeleted)。 – ahawker 2012-01-09 17:47:38

1

拉姆達的方式很好。你也可以使用常規for循環,你可以遍歷for循環在循環中使用的列表,而不像foreach循環。

for (int i = collection.Count-1; i >= 0; i--) 
{ 
    if(ShouldBeDeleted(collection[i]) 
     collection.RemoveAt(i); 
} 

我假設集合是一個arraylist在這裏,如果您使用不同的數據結構,代碼可能會有點不同。

1

您不能從foreach循環內的集合中刪除(除非它是具有特殊枚舉器的非常特殊的集合)。如果集合在枚舉時被修改,則BCL集合將拋出異常。

您可以使用for循環刪除各個元素並相應地調整索引。但是,這樣做可能容易出錯。取決於底層集合的實現,刪除單個元素可能也很昂貴。例如,刪除List<T>的第一個元素將複製列表中的所有重新生成元素。

最好的解決辦法往往是創建基於舊的一個新的集合:

var newCollection = collection.Where(item => !ShouldBeDeleted(item)).ToList(); 

使用ToList()ToArray()創建新的集合或由Where()子句返回的IEnumerable初始化特定集合類型。

1

上落後for循環正向變化:

for (int i = 0; i < collection.Count;) 
    if (ShouldBeDeleted(collection[i])) 
     collection.RemoveAt(i) 
    else 
     i++; 
0

只要使用兩個列表,

首先列出你的原單列表 和 二列表是不應該被刪除的項目。

var filteredItems = new List<ItemType>(); 

foreach(var item in collection){ 
    if(!ShouldBeDeleted(item)) 
     filteredItems.Add(item); 
}