Efficiently deleting item from within 'foreach'

本文关键字:foreach within deleting item from Efficiently | 更新日期: 2023-09-27 18:19:46

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

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

我知道这里至少有一个额外的变量,但为了提高算法的可读性,我加入了它。

Efficiently deleting item from within 'foreach'

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

当你完成后,你有两个集合。新的一个已经删除了项目,旧的一个和以前一样。

就在我完成输入时,我想起有lambda方法可以做到这一点

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

更好的方式?

后向for循环的前向变化:

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

不能从foreach循环内的集合中删除(除非它是一个具有特殊枚举器的非常特殊的集合)。如果在枚举集合时对其进行了修改,则BCL集合将引发异常。

您可以使用for循环来删除各个元素并相应地调整索引。然而,这样做可能容易出错。根据底层集合的实现情况,删除单个元素的成本也可能很高。例如,删除List<T>的第一个元素将复制列表中的所有重映射元素。

最好的解决方案通常是在旧的基础上创建一个新的集合:

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

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

lambda方式很好。您也可以使用正则的for循环,您可以迭代for循环在循环本身中使用的列表,这与foreach循环不同。

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

我假设集合在这里是一个数组列表,如果使用不同的数据结构,代码可能会有点不同。