从'foreach'中有效删除项目

时间:2012-01-09 16:18:57

标签: c# .net collections

目前,我能想到的最好的是:

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;
    }
}

我知道我在这里至少有一个额外的变量,但我把它包括在内以提高算法的可读性。

5 个答案:

答案 0 :(得分:34)

“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);

当你完成后,你有两个收藏。新的项目已删除,旧项目与以前相同。

答案 1 :(得分:14)

正当我完成输入时,我记得有一种lambda方式可以做到。

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

更好的方式?

答案 2 :(得分:2)

向后for循环的正向变体:

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

答案 3 :(得分:1)

lambda方式很好。你也可以使用常规for循环,你可以迭代for循环在循环中使用的列表,不像foreach循环。

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

我假设这里的集合是一个arraylist,如果你使用不同的数据结构,代码可能会有所不同。

答案 4 :(得分:1)

您无法从foreach循环内的集合中删除(除非它是一个非常特殊的集合,具有特殊的枚举器)。如果在枚举集合时修改了集合,BCL集合将抛出异常。

您可以使用for循环删除单个元素并相应地调整索引。但是,这样做可能容易出错。根据底层集合的实现,删除单个元素可能也很昂贵。例如,删除List<T>的第一个元素将复制列表中的所有重新元素。

最好的解决方案通常是根据旧版本创建一个新的集合:

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

使用ToList()ToArray()创建新集合,或从IEnumerable子句返回的Where()初始化您的特定集合类型。