.NET - 从'foreach'循环中的List <t>中删除</t>

时间:2009-05-06 22:43:42

标签: .net foreach remove-method

我的代码想要看起来像这样:

List<Type> Os;

...

foreach (Type o in Os)
    if (o.cond)
        return;  // Quitting early is important for my case!
    else
        Os.Remove(o);

... // Other code

这不起作用,因为当您在该列表上的foreach循环内时,无法从列表中删除:

有没有一种解决问题的常用方法?

如果需要,我可以切换到其他类型。

选项2:

List<Type> Os;

...

while (Os.Count != 0)
     if (Os[0].cond)
         return;
     else
         Os.RemoveAt(0);

... // Other code

丑陋,但它应该有用。

17 个答案:

答案 0 :(得分:58)

您可以向后遍历列表:

for (int i = myList.Count - 1; i >= 0; i--)
{
    if (whatever) myList.RemoveAt(i);
}

当你找到一个你没有删除的项目时想要退出的评论,那么只使用while循环将是最好的解决方案。

答案 1 :(得分:55)

在foreach循环内部,你永远不应该从正在迭代的集合中删除任何东西。它基本上就像锯你所坐的分支一样。

使用你的替代选择。这是要走的路。

答案 2 :(得分:30)

您真的需要在foreach循环中执行此操作吗?

这将获得与您的示例相同的结果,即从列表中删除所有项目,直到匹配条件的第一个项目(或删除所有项目,如果它们都不匹配条件)。

int index = Os.FindIndex(x => x.cond);

if (index > 0)
    Os.RemoveRange(0, index);
else if (index == -1)
    Os.Clear();

答案 3 :(得分:15)

我是一名Java程序员,但这样的工作原理:

List<Type> Os;
List<Type> Temp;
...
foreach (Type o in Os)
    if (o.cond)
        Temp.add(o);
Os.removeAll(Temp);  

答案 4 :(得分:13)

我的分析库遇到了这个问题。我试过这个:

for (int i = 0; i < list.Count; i++)
{                
   if (/*condition*/)
   {
       list.RemoveAt(i);
       i--;
   }
}

这很简单,但我没有想到任何突破点。

答案 5 :(得分:12)

这是最轻松的解决方案,最简单的为什么

问题:

通常,我们从原始列表中删除,这会产生维护列表计数和迭代器位置的问题。

List<Type> Os = ....;
Os.ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

解决方案 - LINQ.ForEach

请注意我添加的所有内容都是ToList()。这将创建一个您执行ForEach的新列表,因此您可以删除原始列表,但继续遍历整个列表。

List<Type> Os = ....;
Os.ToList().ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

解决方案 - 常规foreach

此技术也适用于常规 foreach 语句。

List<Type> Os = ....;
foreach(Type o in Os.ToList()) {
  if(!o.cond) Os.Remove(o);
}

请注意,如果您的原始列表包含struct元素,则此解决方案将无效。

答案 6 :(得分:11)

我知道你要求别的东西,但是如果你想有条件地删除一堆元素你可以使用lambda表达式:

Os.RemoveAll(o => !o.cond);

答案 7 :(得分:9)

 Os.RemoveAll(delegate(int x) { return /// });

答案 8 :(得分:4)

我尝试找到不满足谓词的第一项的索引,并对其执行RemoveRange(0,index)。如果不出意外,应该减少删除电话。

答案 9 :(得分:3)

更新:添加完整性

正如一些人已经回答的那样,你不应该在使用GetEnumerator()(例如foreach)进行迭代时修改集合。该框架阻止您通过抛出异常来做到这一点。对此的通用判断是使用for“手动”迭代(参见其他答案)。请注意索引,这样就不会跳过项目或重新评估相同的项目(使用i--或向后迭代)。

但是,对于您的具体情况,我们可以优化删除操作...原始答案如下。


如果你想要的是删除所有项目,直到满足给定条件(这是你的代码所做的),你可以这样做:

bool exitCondition;

while(list.Count > 0 && !(exitCondition = list[0].Condition))
   list.RemoveAt(0);

或者如果您想使用单个删除操作:

SomeType exitCondition;
int index = list.FindIndex(i => i.Condition);

if(index < 0)
    list.Clear();
else
{
    exitCondition = list[0].State;
    list.RemoveRange(0, count);
}

注意:由于我假设item.Conditionbool,我使用item.State来保存退出条件。

更新:添加边界检查并将退出条件保存到两个示例

答案 10 :(得分:2)

你可以用linq

来做
MyList = MyList.Where(x=>(someCondition(x)==true)).ToList()

答案 11 :(得分:1)

如果您知道列表不是很大,可以使用

foreach (Type o in new List<Type>(Os))
    ....

将创建列表的临时副本。你的remove()调用将不会干扰迭代器。

答案 12 :(得分:1)

Removing items in a list while iterating through it 中对此进行了很好的讨论。

他们建议:

for(int i = 0; i < count; i++)
{
    int elementToRemove = list.Find(<Predicate to find the element>);

    list.Remove(elementToRemove);
}

答案 13 :(得分:1)

查看Enumerable.SkipWhile()

Enumerable.SkipWhile( x => condition).ToList()

一般不会改变列表,让生活更轻松。 :)

答案 14 :(得分:1)

Anzurio的解决方案可能是最直接的解决方案,但如果您不介意在实用程序库中添加一堆接口/类,那么这是另一个简洁的解决方案。

你可以像这样写

List<Type> Os;
...
var en = Os.GetRemovableEnumerator();
while (en.MoveNext())
{
    if (en.Current.Cond)
        en.Remove();
}

将受Java's Iterator<T>.remove启发的以下基础架构放入您的实用程序库:

static class Extensions
{
    public static IRemovableEnumerator<T> GetRemovableEnumerator<T>(this IList<T> l)
    {
        return new ListRemovableEnumerator<T>(l);
    }
}

interface IRemovableEnumerator<T> : IEnumerator<T>
{
    void Remove();
}

class ListRemovableEnumerator<T> : IRemovableEnumerator<T>
{
    private readonly IList<T> _list;
    private int _count;
    private int _index;
    public ListRemovableEnumerator(IList<T> list)
    {
        _list = list;
        _count = list.Count;
        _index = -1;
    }

    private void ThrowOnModification()
    {
        if (_list.Count != _count)
            throw new InvalidOperationException("List was modified after creation of enumerator");
    }
    public void Dispose()
    {
    }

    public bool MoveNext()
    {
        ThrowOnModification();
        if (_index + 1 == _count)
            return false;
        _index++;
        return true;
    }

    public void Reset()
    {
        ThrowOnModification();
        _index = -1;
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public T Current
    {
        get { return _list[_index]; }
    }

    public void Remove()
    {
        ThrowOnModification();
        _list.RemoveAt(_index);
        _index--;
        _count--;
    }
}

答案 15 :(得分:0)

我遇到了同样的问题并使用以下方法解决了这个问题:

foreach (Type o in (new List(Os))) { if (something) Os.Remove(o); }

它遍历列表的副本并从原始列表中删除。

答案 16 :(得分:0)

在列表中添加要删除的项目,然后使用RemoveAll

删除这些项目
List<Type> Os;
List<Type> OsToRemove=new List<Type>();
...
foreach (Type o in Os){
    if (o.cond)
        return;
    else
        OsToRemove.Add(o);
}
Os.RemoveAll(o => OsToRemove.Contains(o));