C# 有效地从“foreach”中删除项目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8791557/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 04:27:15  来源:igfitidea点击:

Efficiently deleting item from within 'foreach'

c#.netcollections

提问by Daniel Mo?mondor

For now, the best I could think of is:

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

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

I know that I have at least one extra variable here, but I included it to improve the readability of the algorithm.

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

采纳答案by Eric Lippert

The "RemoveAll" method is best.

“RemoveAll”方法是最好的。

Another common technique is:

另一种常见的技术是:

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

Another common technique is to use a "for" loop, but make sure you go backwards:

另一种常见的技术是使用“for”循环,但请确保您倒退

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

Another common technique is to add the items that are notbeing removed to a new collection:

另一种常用技术是将删除的项目添加到新集合中:

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

And now you have two collections. A technique I particularly like if you want to end up with two collections is to use immutable data structures. With an immutable data structure, "removing" an item does not change the data structure; it gives you back a new data structure (that re-uses bits from the old one, if possible) that does not have the item you removed. With immutable data structures you are not modifying the thing you're iterating over, so there's no problem:

现在你有两个集合。如果您想以两个集合结束,我特别喜欢的一种技术是使用不可变数据结构。对于不可变的数据结构,“删除”一个项目不会改变数据结构;它会给你一个新的数据结构(如果可能的话,它会重新使用旧的数据结构),它没有你删除的项目。使用不可变数据结构,您不会修改正在迭代的内容,因此没有问题:

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

or

或者

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

And when you're done, you have two collections. The new one has the items removed, the old one is the same as it ever was.

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

回答by Daniel Mo?mondor

Just as I finished typing I remembered that there is lambda-way to do it.

就在我打完字时,我想起了 lambda 方法可以做到这一点。

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

Better way?

更好的方法?

回答by Mitch

The lambda way is good. You could also use a regular for loop, you can iterate lists that a for loop uses within the loop itself, unlike a foreach loop.

lambda 方法很好。您还可以使用常规 for 循环,您可以迭代 for 循环在循环本身内使用的列表,这与 foreach 循环不同。

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

I am assuming that collection is an arraylist here, the code might be a bit different if you are using a different data structure.

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

回答by Martin Liversage

You cannot delete from a collection inside a foreachloop (unless it is a very special collection having a special enumerator). The BCL collections will throw exceptions if the collection is modified while it is being enumerated.

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

You could use a forloop to delete individual elements and adjust the index accordingly. However, doing that can be error prone. Depending on the implementation of the underlying collection it may also be expensive to delete individual elements. For instance deleting the first element of a List<T>will copy all the remaning elements in the list.

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

The best solution is often to create a new collection based on the old:

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

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

Use ToList()or ToArray()to create the new collection or initialize your specific collection type from the IEnumerablereturned by the Where()clause.

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

回答by phoog

A forward variation on the backward forloop:

反向for循环的正向变化:

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