C# 如何在迭代时从通用列表中删除元素?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1582285/
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-06 19:02:08  来源:igfitidea点击:

How to remove elements from a generic list while iterating over it?

c#listloopsgenericskey-value

提问by InvertedAcceleration

I am looking for a better patternfor working with a list of elements which each need processed and then depending on the outcome are removed from the list.

我正在寻找一种更好的模式来处理每个需要处理的元素列表,然后根据结果从列表中删除。

You can't use .Remove(element)inside a foreach (var element in X)(because it results in Collection was modified; enumeration operation may not execute.exception)... you also can't use for (int i = 0; i < elements.Count(); i++)and .RemoveAt(i)because it disrupts your current position in the collection relative to i.

您不能.Remove(element)在 a 内使用foreach (var element in X)(因为它会导致Collection was modified; enumeration operation may not execute.异常)...您也不能使用for (int i = 0; i < elements.Count(); i++)并且.RemoveAt(i)因为它会破坏您在集合中相对于i.

Is there an elegant way to do this?

有没有一种优雅的方法来做到这一点?

采纳答案by Ahmad Mageed

Iterate your list in reverse with a for loop:

使用 for 循环反向迭代您的列表:

for (int i = safePendingList.Count - 1; i >= 0; i--)
{
    // some code
    // safePendingList.RemoveAt(i);
}

Example:

例子:

var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
    if (list[i] > 5)
        list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));

Alternately, you can use the RemoveAll methodwith a predicate to test against:

或者,您可以使用带有谓词的RemoveAll 方法来测试:

safePendingList.RemoveAll(item => item.Value == someValue);

Here's a simplified example to demonstrate:

这是一个简单的示例来演示:

var list = new List<int>(Enumerable.Range(1, 10));
Console.WriteLine("Before:");
list.ForEach(i => Console.WriteLine(i));
list.RemoveAll(i => i > 5);
Console.WriteLine("After:");
list.ForEach(i => Console.WriteLine(i));

回答by Martin Liversage

I would reassign the list from a LINQ query that filtered out the elements you didn't want to keep.

我会从过滤掉您不想保留的元素的 LINQ 查询中重新分配列表。

list = list.Where(item => ...).ToList();

Unless the list is very large there should be no significant performance problems in doing this.

除非列表非常大,否则在执行此操作时应该没有显着的性能问题。

回答by Jan

A simple and straightforward solution:

一个简单直接的解决方案:

Use a standard for-loop running backwardson your collection and RemoveAt(i)to remove elements.

使用标准的 for 循环在您的集合上向后运行并RemoveAt(i)删除元素。

回答by Etienne Brouillard

Using the ToArray() on a generic list allows you to do a Remove(item) on your generic List:

在通用列表上使用 ToArray() 允许您在通用列表上执行 Remove(item) :

        List<String> strings = new List<string>() { "a", "b", "c", "d" };
        foreach (string s in strings.ToArray())
        {
            if (s == "b")
                strings.Remove(s);
        }

回答by JulianR

Select the elements you dowant rather than trying to remove the elements you don'twant. This is so much easier (and generally more efficient too) than removing elements.

选择您的元素想要,而不是试图消除你的元素想要的。这比删除元素要容易得多(通常也更有效)。

var newSequence = (from el in list
                   where el.Something || el.AnotherThing < 0
                   select el);

I wanted to post this as a comment in response to the comment left by Michael Dillon below, but it's too long and probably useful to have in my answer anyway:

我想将此作为评论发布,以回应下面迈克尔·狄龙 (Michael Dillon) 留下的评论,但它太长了,无论如何在我的回答中可能有用:

Personally, I'd never remove items one-by-one, if you do need removal, then call RemoveAllwhich takes a predicate and only rearranges the internal array once, whereas Removedoes an Array.Copyoperation for every element you remove. RemoveAllis vastly more efficient.

就个人而言,我永远不会一个一个地删除项目,如果您确实需要删除,则调用RemoveAllwhich 需要一个谓词并且只重新排列内部数组一次,而RemoveArray.Copy您删除的每个元素执行一个操作。RemoveAll效率更高。

And when you're backwards iterating over a list, you already have the index of the element you want to remove, so it would be far more efficient to call RemoveAt, because Removefirst does a traversal of the list to find the index of the element you're trying to remove, but you already know that index.

并且当您向后迭代列表时,您已经有了要删除的元素的索引,因此调用 会更有效率RemoveAt,因为Remove首先遍历列表以找到您要删除的元素的索引' 正在尝试删除,但您已经知道该索引。

So all in all, I don't see any reason to ever call Removein a for-loop. And ideally, if it is at all possible, use the above code to stream elements from the list as needed so no second data structure has to be created at all.

总而言之,我看不出有任何理由Remove在 for 循环中调用。理想情况下,如果可能的话,使用上面的代码根据需要从列表中流式传输元素,这样根本不需要创建第二个数据结构。

回答by jedesah

Reverse iteration should be the first thing to come to mind when you want to remove elements from a Collection while iterating over it.

当您想在迭代集合时从集合中删除元素时,首先要想到的是反向迭代。

Luckily, there is a more elegant solution than writing a for loop which involves needless typing and can be error prone.

幸运的是,有一个比编写 for 循环更优雅的解决方案,它涉及不必要的输入并且可能容易出错。

ICollection<int> test = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

foreach (int myInt in test.Reverse<int>())
{
    if (myInt % 2 == 0)
    {
        test.Remove(myInt);
    }
}

回答by Mauricio Ramalho

List<T> TheList = new List<T>();

TheList.FindAll(element => element.Satisfies(Condition)).ForEach(element => TheList.Remove(element));

回答by StuartQ

Using .ToList() will make a copy of your list, as explained in this question: ToList()-- Does it Create a New List?

使用 .ToList() 将复制您的列表,如以下问题中所述: ToList()--它是否创建新列表?

By using ToList(), you can remove from your original list, because you're actually iterating over a copy.

通过使用 ToList(),您可以从原始列表中删除,因为您实际上是在迭代副本。

foreach (var item in listTracked.ToList()) {    

        if (DetermineIfRequiresRemoval(item)) {
            listTracked.Remove(item)
        }

     }

回答by Paul Nelson Baker

I found myself in a similar situation where I had to remove every nthelement in a given List<T>.

我发现自己在类似的情况下,我不得不删除每n元素在给定的List<T>

for (int i = 0, j = 0, n = 3; i < list.Count; i++)
{
    if ((j + 1) % n == 0) //Check current iteration is at the nth interval
    {
        list.RemoveAt(i);
        j++; //This extra addition is necessary. Without it j will wrap
             //down to zero, which will throw off our index.
    }
    j++; //This will always advance the j counter
}

回答by warrens

I wishthe "pattern" was something like this:

希望“模式”是这样的:

foreach( thing in thingpile )
{
    if( /* condition#1 */ )
    {
        foreach.markfordeleting( thing );
    }
    elseif( /* condition#2 */ )
    {
        foreach.markforkeeping( thing );
    }
} 
foreachcompleted
{
    // then the programmer's choices would be:

    // delete everything that was marked for deleting
    foreach.deletenow(thingpile); 

    // ...or... keep only things that were marked for keeping
    foreach.keepnow(thingpile);

    // ...or even... make a new list of the unmarked items
    others = foreach.unmarked(thingpile);   
}

This would align the code with the process that goes on in the programmer's brain.

这将使代码与程序员大脑中进行的过程保持一致。