C# 迭代列表并从中删除项目的最佳方法?
我需要遍历C# 迭代列表并从中删除项目的最佳方法?,c#,list,iteration,C#,List,Iteration,我需要遍历列表,并删除满足特定条件的项 我看到了这个答案(): 使用for循环反向迭代列表: for (int i = safePendingList.Count - 1; i >= 0; i--) { // some code // safePendingList.RemoveAt(i); } 例如: var list = new List<int>(Enumerable.Range(1, 10)); for (int i = list.Count - 1;
列表
,并删除满足特定条件的项
我看到了这个答案():
使用for循环反向迭代列表:
for (int i = safePendingList.Count - 1; i >= 0; i--)
{
// some code
// safePendingList.RemoveAt(i);
}
例如:
var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i] > 5)
list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));
一种方法比另一种好吗
编辑:
我不想使用
RemoveAll(…)
,因为在条件之前,循环中有大量代码。要删除具有特定条件的项,可以使用
list.RemoveAll(item => item > 5);
而不是
var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i] > 5)
list.RemoveAt(i);
}
var list=新列表(可枚举范围(1,10));
对于(int i=list.Count-1;i>=0;i--)
{
如果(列表[i]>5)
列表。删除(i);
}
问题在于,foreach
无法在修改集合时使用集合,这将导致无效操作异常
在foreach
内部使用.ToList()
可以避免这种情况,但这样会复制整个集合,从而导致在集合中循环两次(第一次是复制所有元素,第二次是过滤)
for
循环是更好的选择,因为我们可以遍历一次集合。除此之外,您还可以使用LINQ
Where()
方法筛选集合,这些方法包括:
var myFilteredCollection = myList.Where(i => i <= 5).ToList();
并在LINQ查询中使用它:
var myFilteredCollection = myList.Where(i => IsItemOk(i)).ToList();
另一种选择是反过来做。根据条件创建新集合并添加元素。然后可以使用foreach
循环:
var newList = new List<MyObject>(myList.Count);
foreach (var item in myList)
{
// if certain condition does not apply:
newList.Add(item);
}
var newList=新列表(myList.Count);
foreach(myList中的var项)
{
//如果某些条件不适用:
新建列表。添加(项);
}
附言
但是正如前面提到的那样,.RemoveAll()
更好,因为这样你就不必“重新发明轮子”你可以随意地在列表上循环,而for
循环是最有效的循环:
for (int i = safePendingList.Count - 1; i >= 0; --i)
if (condition)
safePendingList.RemoveAt(i);
如果要在范围内(而不是在整个列表中)删除,只需修改for loop:
for (int i = safePendingList.Count - 1; i >= 0; i--)
{
// some code
// safePendingList.RemoveAt(i);
}
或者,如果必须删除正向循环中的项目:
然而,在许多情况下,最合理的计划就是让.Net为您工作:
两种方法均未显示显著差异
for
和RemoveAll
看起来快一点(更有可能是因为使用Remove
复制列表,需要重新创建列表…使用值类型,这也意味着需要加倍的内存)。我做了一些分析:
public static void MethodA()
{
var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i] > 5)
list.RemoveAt(i);
}
}
public static void MethodB()
{
var list = new List<int>(Enumerable.Range(1, 10));
foreach (var item in list.ToList())
{
if (item > 5)
list.Remove(item);
}
}
public static void MethodC()
{
var list = new List<int>(Enumerable.Range(1, 10));
list.RemoveAll(x => x > 5);
}
public static void Measure(string meas, Action act)
{
GC.Collect();
GC.WaitForPendingFinalizers();
var st = new Stopwatch();
st.Start();
for (var i = 0; i < 10000000; i++)
act();
st.Stop();
Console.WriteLine($"{meas}: {st.Elapsed}");
}
static void Main(string[] args)
{
Measure("A", MethodA);
Measure("B", MethodB);
Measure("C", MethodC);
}
你只需要扭转这种状况
我不是建议使用这种方法,只是证明
foreach
不一定比慢,因为另一种方法可以是foreach列表,将项设置为null,然后执行linq remove to(item=>null),如前所述,这取决于您选择更适合您的方式“更好”如在什么?演出记忆?易读性?
的效率不低于foreach
。您可以使用RemoveAll
,而不是循环,它具有更好的性能:您不能从foreach中使用的同一列表中删除项,您将得到一个异常(System.InvalidoOperationException)。LINQ中没有RemoveAll()
这样的东西。@poke这对于数组肯定不是真的,对于其中的foreach
进行了高度优化。myList.Where(x=>x>5)
?@arrowd-要获得相同的结果,它必须是myList=myList.Where(x=>x我不想使用LINQ,因为条件很复杂,并且是作为循环构建的progresses@arrowd创建列表的新副本时省略某些项目与从现有列表中删除项目不同。@arrowd他说的是使用RemoveAll()
,而不是RemoveAt())
没错,如果你想知道哪匹马跑得更快……通常最好的策略是让.Net来完成它的工作-删除所有的+1@DmitryBychenko我从理论上期待这个结果,但是是的,没有什么比赛马更好:-)我相信你已经知道了,但是因为“…但我知道,for
的效率低于foreach
。。。“在这个问题上,确保相反情况成立的最佳方法是让马儿运行。10个元素的列表对于这种测试来说太小了,因为内存分配等的开销会扭曲结果。尝试使用100万个元素的列表(删除索引小于500000的所有项目)MethodA
和MethodB
之间的性能差异很可能是由于使用了Remove
而不是RemoveAt
,Remove
必须在整个列表中搜索有问题的项目,而使用RemoveAt
,您会告诉它确切的位置。需要记住的一点是:使用RemoveAt();
是最接近于高阶过滤器的函数,而且由于其简短易读,RemoveAll
的主要优点是,无论需要删除多少项或哪些项,它都需要线性时间。基于RemoveAt
的解决方案可能需要二次时间,因为需要移动大部分内容重复列出。你把我的答案塞进了你的:-\
// No Enumarable.Range(1, 10) - put them into "for"
for (int i = Math.Min(11, safePendingList.Count - 1); i >= 1; --i)
if (condition)
safePendingList.RemoveAt(i);
for (int i = 0; i < safePendingList.Count;) // notice ++i abscence
if (condition)
safePendingList.RemoveAt(i);
else
i += 1; // ++i should be here
// safePendingList.ToList() - CPU and Memory overhead (copying)
foreach (var item in safePendingList.ToList()) {
if (condition)
myList.Remove(item); // Overhead: searching
}
safePendingList.RemoveAll(item => condition);
public static void MethodA()
{
var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
if (list[i] > 5)
list.RemoveAt(i);
}
}
public static void MethodB()
{
var list = new List<int>(Enumerable.Range(1, 10));
foreach (var item in list.ToList())
{
if (item > 5)
list.Remove(item);
}
}
public static void MethodC()
{
var list = new List<int>(Enumerable.Range(1, 10));
list.RemoveAll(x => x > 5);
}
public static void Measure(string meas, Action act)
{
GC.Collect();
GC.WaitForPendingFinalizers();
var st = new Stopwatch();
st.Start();
for (var i = 0; i < 10000000; i++)
act();
st.Stop();
Console.WriteLine($"{meas}: {st.Elapsed}");
}
static void Main(string[] args)
{
Measure("A", MethodA);
Measure("B", MethodB);
Measure("C", MethodC);
}
public static void MethodD()
{
var originalList = new List<int>(Enumerable.Range(1, 1000));
var list = new List<int>(originalList.Count);
foreach (var item in originalList)
{
if (item <= 500)
list.Add(item);
}
list.Capacity = list.Count;
}