Neither method shows significant differences. for
and RemoveAll
looks a bit faster (more likely because using Remove
you are duplicating your list and it needs to recreate it... using value types, this also means doubling the needed memory). I made some profiling:
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);
}
The result:
A: 00:00:04.1179163
B: 00:00:07.7417853
C: 00:00:04.3525255
Other runs give even more similar results (note that this is for 10 million iterations on a 10 item list [thus doing 100 million operations]... a difference of ~3 seconds is not what I'd call "significant", but your mileage may vary)
So go with what you think you can read better (I'd personally use the RemoveAll
method)
Note: as you grow your list (this is just 10 items), MethodB
(the foreach
one that duplicates the list) will be slower and the differences will be higher, since the duplication of the list will be more costly. For example, doing a list of 1,000 items (with the condition being on 500 instead of 5) and 100,000 iterations, makes MethodB
be 34 seconds, while both others run under 2 seconds on my machine. So yes, definitely don't use MethodB
:-)
This doesn't mean that foreach
is less efficient than for
, it only means that for using foreach
to remove items from a list, you need to duplicate said list, and that is costly.
This other method (using foreach
) should be about as fast as the for
or RemoveAll
methods (it's a tiny bit slower in my profiling -because it allocates a new list, I figure-, but pretty much negligible):
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;
}
You just need to reverse the condition.
I'm not suggesting this method, just proving that foreach
is not necessarily slower than for