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 个号码。
所以在处理列表时,完全取决于你所说的“行为相同”。
如果我们只考虑最终列表中还剩下哪些数字,最终结果是否相同?
是的。
总运行时间是否相同?
没有
我想根据条件从列表中删除一个元素。问题是我无法在向前迭代时删除它,因为它的数组类似于实现,这会改变列表结构和计数。
所以我读了这个答案,我确信这应该是解决方案: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 个号码。
所以在处理列表时,完全取决于你所说的“行为相同”。
如果我们只考虑最终列表中还剩下哪些数字,最终结果是否相同?
是的。
总运行时间是否相同?
没有