首先,由于明显的原因,我知道这是不可能的。
foreach(string item in myListOfStrings) {
myListOfStrings.Remove(item);
}
上面的片段是我见过的最恐怖的事情之一。那么,您如何实现呢?您可以使用
for
向后遍历列表,但我也不喜欢这种解决方案。我想知道的是:是否存在从当前列表返回IEnumerable的方法/扩展,例如 float 副本? LINQ有许多扩展方法可以做到这一点,但是您总是必须使用它来做一些事情,例如过滤(在哪里,需要...)。
我期待着这样的事情:
foreach(string item in myListOfStrings.Shadow()) {
myListOfStrings.Remove(item);
}
其中.Shadow()是:
public static IEnumerable<T> Shadow<T>(this IEnumerable<T> source) {
return new IEnumerable<T>(source);
// or return source.Copy()
// or return source.TakeAll();
}
示例
foreach(ResponseFlags flag in responseFlagsList.Shadow()) {
switch(flag) {
case ResponseFlags.Case1:
...
case ResponseFlags.Case2:
...
}
...
this.InvokeSomeVoidEvent(flag)
responseFlagsList.Remove(flag);
}
解决方案
这就是我解决问题的方法,它就像一种魅力:
public static IEnumerable<T> Shadow<T>(this IEnumerable<T> source) where T: new() {
foreach(T item in source)
yield return item;
}
它不是那么快(显然),但是它是安全的,而且正是我打算要做的。
最佳答案
由于列表是如何实现的,因此从列表中1到1删除多个元素是C#反模式。
当然,可以使用for循环(而不是foreach)来完成。或者可以通过复制列表来完成。但是,这就是为什么不应该这样做的原因。在100000个随机整数的列表上,这在我的机器上花费2500毫秒:
foreach (var x in listA.ToList())
if (x % 2 == 0)
listA.Remove(x);
这需要1250毫秒:
for (int i = 0; i < listA.Count; i++)
if (listA[i] % 2 == 0)
listA.RemoveAt(i--);
而这两个分别需要5毫秒和2毫秒:
listB = listB.Where(x => x % 2 != 0).ToList();
listB.RemoveAll(x => x % 2 == 0);
这是因为当您从列表中删除元素时,实际上是从数组中删除,这是O(N)时间,因为您需要将已删除元素后的每个元素向左移动一个位置。平均而言,这将是N/2个元素。
Remove(element)还需要在删除元素之前先找到它。因此,Remove(element)实际上总是要执行N个步骤-
elementindex
步骤来查找元素,而N - elementindex
步骤来删除元素-总共要执行N个步骤。RemoveAt(index)不必查找元素,但仍然必须移动基础数组,因此平均而言,RemoveAt为N/2个步骤。
无论哪种方式,最终结果都是O(N ^ 2)复杂度,因为您最多要删除N个元素。
相反,您应该使用Linq,它会在O(N)时间修改整个列表,或者自己滚动列表,但您不应在循环中使用Remove(或RemoveAt)。
关于c# - 迭代时从列表中删除项目/从列表中添加项目,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17233558/