有人知道List的Where和FindAll方法之间的速度差异吗?我知道Where是IEnumerable接口的一部分,而FindAll是List的一部分,我只是想知道哪个更快。
有人知道List的Where和FindAll方法之间的速度差异吗?我知道Where是IEnumerable接口的一部分,而FindAll是List的一部分,我只是想知道哪个更快。
List<T>类的FindAll方法实际上构造一个新列表对象,并将结果添加到其中。而IEnumerable<T>的Where扩展方法将简单地遍历现有列表,并生成匹配结果的枚举,而不创建或添加任何东西(除了枚举器本身)。
对于小型数据集,这两种方法可能表现相似。但是,对于大型数据集,Where应该优于FindAll,因为用来包含结果的新列表将动态增长以容纳更多结果。当匹配结果数量增加时,FindAll的内存使用率也会呈指数级增长,而Where应该具有恒定最小的内存使用率(本身而言……不包括您对结果的处理)。
Count
或更改成员,或者要多次迭代它)。虽然 Where()
比 FindAll()
更好,但 FindAll()
在比较 Where().ToList()
时更好。 - Jon Hannausing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace Tests
{
public class Dummy
{
public int Val;
public Dummy(int val)
{
Val = val;
}
}
public class WhereOrFindAll
{
const int ElCount = 20000000;
const int FilterVal =1000;
const int MaxVal = 2000;
const bool CheckSum = true; // Checks sum of elements in list of resutls
static List<Dummy> list = new List<Dummy>();
public delegate void FuncToTest();
public static long TestTicks(FuncToTest function, string msg)
{
Stopwatch watch = new Stopwatch();
watch.Start();
function();
watch.Stop();
Console.Write("\r\n"+msg + "\t ticks: " + (watch.ElapsedTicks));
return watch.ElapsedTicks;
}
static void Check(List<Dummy> list)
{
if (!CheckSum) return;
Stopwatch watch = new Stopwatch();
watch.Start();
long res=0;
int count = list.Count;
for (int i = 0; i < count; i++) res += list[i].Val;
for (int i = 0; i < count; i++) res -= (long)(list[i].Val * 0.3);
watch.Stop();
Console.Write("\r\n\nCheck sum: " + res.ToString() + "\t iteration ticks: " + watch.ElapsedTicks);
}
static void Check(IEnumerable<Dummy> ieNumerable)
{
if (!CheckSum) return;
Stopwatch watch = new Stopwatch();
watch.Start();
IEnumerator<Dummy> ieNumerator = ieNumerable.GetEnumerator();
long res = 0;
while (ieNumerator.MoveNext()) res += ieNumerator.Current.Val;
ieNumerator=ieNumerable.GetEnumerator();
while (ieNumerator.MoveNext()) res -= (long)(ieNumerator.Current.Val * 0.3);
watch.Stop();
Console.Write("\r\n\nCheck sum: " + res.ToString() + "\t iteration ticks :" + watch.ElapsedTicks);
}
static void Generate()
{
if (list.Count > 0)
return;
var rand = new Random();
for (int i = 0; i < ElCount; i++)
list.Add(new Dummy(rand.Next(MaxVal)));
}
static void For()
{
List<Dummy> resList = new List<Dummy>();
int count = list.Count;
for (int i = 0; i < count; i++)
{
if (list[i].Val < FilterVal)
resList.Add(list[i]);
}
Check(resList);
}
static void Foreach()
{
List<Dummy> resList = new List<Dummy>();
int count = list.Count;
foreach (Dummy dummy in list)
{
if (dummy.Val < FilterVal)
resList.Add(dummy);
}
Check(resList);
}
static void WhereToList()
{
List<Dummy> resList = list.Where(x => x.Val < FilterVal).ToList<Dummy>();
Check(resList);
}
static void WhereIEnumerable()
{
Stopwatch watch = new Stopwatch();
IEnumerable<Dummy> iEnumerable = list.Where(x => x.Val < FilterVal);
Check(iEnumerable);
}
static void FindAll()
{
List<Dummy> resList = list.FindAll(x => x.Val < FilterVal);
Check(resList);
}
public static void Run()
{
Generate();
long[] ticks = { 0, 0, 0, 0, 0 };
for (int i = 0; i < 10; i++)
{
ticks[0] += TestTicks(For, "For \t\t");
ticks[1] += TestTicks(Foreach, "Foreach \t");
ticks[2] += TestTicks(WhereToList, "Where to list \t");
ticks[3] += TestTicks(WhereIEnumerable, "Where Ienum \t");
ticks[4] += TestTicks(FindAll, "FindAll \t");
Console.Write("\r\n---------------");
}
for (int i = 0; i < 5; i++)
Console.Write("\r\n"+ticks[i].ToString());
}
}
class Program
{
static void Main(string[] args)
{
WhereOrFindAll.Run();
Console.Read();
}
}
}
结果(ticks) - 检查和已启用(对结果进行某些操作),模式:发布而无调试(CTRL+F5):
- 16,222,276 (for ->list)
- 17,151,121 (foreach -> list)
- 4,741,494 (where ->list)
- 27,122,285 (where ->ienum)
- 18,821,571 (findall ->list)
校验和已禁用(完全不使用返回的列表):
- 10,885,004 (for ->list)
- 11,221,888 (foreach ->list)
- 18,688,433 (where ->list)
- 1,075 (where ->ienum)
- 13,720,243 (findall ->list)
你的结果可能会略有不同,要得到真正的结果需要更多迭代。
更新(来自评论):查看那段代码后,我同意,.Where 在最坏情况下应该具有相等的性能,但几乎总是更好。
原始答案:
.FindAll()
应该更快,它利用已知的 List 大小并通过简单的 for
循环遍历内部数组。 .Where()
必须启动一个枚举器(在这种情况下称为 WhereIterator 的封闭框架类),并以不太具体的方式完成相同的工作。
请记住,.Where() 是可枚举的,不会主动创建内存中的 List 并填充它。 它更像是流式传输,因此在处理非常大的数据时,内存使用可能会有显着差异。 另外,您可以更快地使用 4.0 中的 .Where() 方法以并行方式开始使用结果。
.Where()
重载中的案例堆栈返回,谢谢!浏览那段代码后,我同意.Where()
应该在最坏的情况下具有相等的性能,但几乎总是更好。此外,如果没有人花费额外的时间来教育他人,例如您和这些评论,stackoverflow将毫无用处,+1教会了我一些东西。 - Nick CraverWhere
比FindAll
快得多。无论列表有多大,Where
所需的时间都是完全相同的。
当然,Where
只是创建一个查询,它不像FindAll
一样创建一个列表。
jrista的回答很有道理。然而,新列表添加了相同的对象,因此只是基于现有对象的引用增长,这不应该那么慢。 只要3.5 / Linq扩展是可能的,无论如何都更好。 当限制在2.0时,FindAll更有意义。