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

你不能在foreach (var element in X)中使用.Remove(element)(因为它会导致Collection被修改;枚举操作可能无法执行。例外)……你也不能使用for (int I = 0;i < elements.Count();i++)和. removeat (i),因为它破坏了你在集合中相对于i的当前位置。

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


当前回答

假设predicate是一个元素的布尔属性,如果它为真,则该元素应该被移除:

        int i = 0;
        while (i < list.Count())
        {
            if (list[i].predicate == true)
            {
                list.RemoveAt(i);
                continue;
            }
            i++;
        }

其他回答

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

在你的集合上使用一个标准的for循环,并使用RemoveAt(i)来删除元素。

当您希望在迭代Collection时从其中删除元素时,首先想到的应该是反向迭代。

幸运的是,有一个比编写for循环更优雅的解决方案,因为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);
    }
}

在遍历列表时从列表中删除项的最佳方法是使用RemoveAll()。但是人们编写的主要问题是他们必须在循环中做一些复杂的事情和/或有复杂的比较情况。

解决方案是仍然使用RemoveAll(),但使用以下符号:

var list = new List<int>(Enumerable.Range(1, 10));
list.RemoveAll(item => 
{
    // Do some complex operations here
    // Or even some operations on the items
    SomeFunction(item);
    // In the end return true if the item is to be removed. False otherwise
    return item > 5;
});

如果决定删除哪些项的函数没有副作用,也没有改变项(这是一个纯函数),一个简单有效的(线性时间)解决方案是:

list.RemoveAll(condition);

如果有副作用,我会使用如下方法:

var toRemove = new HashSet<T>();
foreach(var item in items)
{
     ...
     if(condition)
          toRemove.Add(item);
}
items.RemoveAll(toRemove.Contains);

这仍然是线性时间,假设哈希是好的。但是由于hashset,它增加了内存使用。

最后,如果你的列表只是一个IList<T>,而不是一个list <T>,我建议我的答案,我怎么能做这个特殊的foreach迭代器?与许多其他答案的二次运行时相比,给出IList<T>的典型实现,它将具有线性运行时。

因为任何移除都是在你可以使用的条件下进行的

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