c# 在遍历列表时删除元素 - 向后迭代或使用 i-- 或使用 linq 同时迭代和删除?

c# Removing element while traversing list- iterate backwards or use i-- or using linq to iterate and remove at the same time?

我想根据条件从列表中删除一个元素。问题是我无法在向前迭代时删除它,因为它的数组类似于实现,这会改变列表结构和计数。

所以我读了这个答案,我确信这应该是解决方案:how-to-remove-elements-from-a-generic-list-while-iterating-over-it

但是有人给我反馈说这不容易理解,因为我们正在向后迭代它,而我们应该向前迭代并在满足删除条件时执行 i--。

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

我对这种方法进行了单元测试,结果符合预期,但我仍然担心所有可能的情况都是一样的,因为我在任何论坛上都找不到这个答案,而且每个人都建议向后迭代。

谁能告诉我这两种解决方案的行为是相同还是不同?

我建议不要只使用索引在迭代中从列表中删除项目。简单地将 Linq Select 与 where 条件一起使用。这实际上会为您生成一个新列表。

var list = new List<int>(Enumerable.Range(1, 10));
var newList= list.Select(number=>number<=5).ToList();

现在 newList 将只包含满足您在 lambda 表达式中提供的条件的项目。

就地从列表中删除项目的首选方法是使用这种代码反向执行:

int index = list.Count - 1;
while (index >= 0)
{
    if (expression identifying item to remove)
        list.RemoveAt(index);
    index--;
}

现在,请记住,这仅适用于列表中的移除 就地 ,这意味着您不想构建要保留的项目的新列表.如果可以,请构建一个包含要保留的项目的新列表,LINQ 表达式可能更好。

那么,为什么 首选上述方法?

好吧,考虑一下。 “从列表中删除项目”到底是什么意思? C#中aList<T>的底层数据结构是数组。从数组中删除一个项目并不能真正完成,但您可以做的是将值移动到数组中。要从数组中“删除”一个项目,您可以将其后的所有项目向上移动一个索引。

此操作将花费相对于其后的项目数的时间。

因此,让我们看看在“前向方法”中执行此操作。在这种方法中,您将从索引 0 开始,每次找到要保留的项目时向上移动,但您也可以通过将其后的每个项目向下移动一个元素来删除该项目。

让我们举一个简单的例子,你有一个从 1 到 10 的每个数字的列表,你想删除每个偶数元素值(2、4、6、8 等)。

所以你有这个:

1 2 3 4 5 6 7 8 9 10

要删除的第一个项目是 2,这将必须移动它后面的每个数字,从 3 到 10,向下一个索引。即移动了8个数字。

下一个项目是4,它必须向下移动5到10,即6个数字。现在我们一共搬了14个号。

接下来是 6,从 7 到 10 向下移动,即 4 个数字,现在我们最多移动 18 个数字。

接下来是 8,移动 9 和 10,2 个号码,我们最多移动 20 个号码。

由于我们要删除的最后一个数字 10 后面没有数字,所以我们总共移动了 20 个数字。

现在让我们反过来做。

首先我们看10,我们要去掉这个,后面没有数字,所以不移动。

接下来我们要删除的是 8,后面只有一个数字,所以我们将 9 下移一位。 10 已被删除,因此不再存在。

接下来我们要去掉的是6,后面有两个数字,7和9,把它们往下移,一共移了3个数字。

去掉4,后面是5、7、9,一共搬了6个号。

删除 2 - 3, 5, 7 和 9 紧随其后所以 现在我们总共移动了 10 个号码。

所以在处理列表时,完全取决于你所说的“行为相同”。

如果我们只考虑最终列表中还剩下哪些数字,最终结果是否相同?

是的。

总运行时间是否相同?

没有