Well, FindAll
将匹配的元素复制到新列表中,而Where
仅返回一个延迟计算的序列 - 不需要复制。
因此我期望Where
略快于FindAll
即使结果序列被完全评估 - 当然还有惰性评估策略Where
意味着如果您只查看(比如说)第一个匹配项,则不需要检查列表的其余部分。 (正如马修指出的,维护状态机是有工作的Where
。然而,这只会有固定的内存成本 - 而构建新列表可能需要多个数组分配等。)
基本上,FindAll(predicate)
更接近于Where(predicate).ToList()
不仅仅是Where(predicate)
.
只是为了对马修的回答做出更多反应,我认为他没有足够彻底地测试它。他的谓词碰巧选择了half这几项。这是一个简短但完整的程序,它测试相同的列表,但使用三个不同的谓词 - 一个不选择任何项目,一个选择所有项目,一个选择其中的一半。在每种情况下,我都会运行测试五十次以获得更长的时间。
我在用着Count()
以确保Where
结果得到充分评价。结果显示,收集了大约一半的结果,两者并驾齐驱。没有收集到任何结果,FindAll
获胜。收藏all结果,Where
获胜。我觉得这很有趣:随着找到越来越多的匹配,所有解决方案都会变得更慢:FindAll
有更多的复印工作要做,并且Where
必须返回匹配的值,而不是仅仅在MoveNext()
执行。然而,FindAll
变得比慢更快Where
确实如此,因此失去了早期的领先优势。很有意思。
Results:
FindAll: All: 11994
Where: All: 8176
FindAll: Half: 6887
Where: Half: 6844
FindAll: None: 3253
Where: None: 4891
(使用 /o+ /debug- 编译并从命令行运行,.NET 3.5。)
Code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
class Test
{
static List<int> ints = Enumerable.Range(0, 10000000).ToList();
static void Main(string[] args)
{
Benchmark("All", i => i >= 0); // Match all
Benchmark("Half", i => i % 2 == 0); // Match half
Benchmark("None", i => i < 0); // Match none
}
static void Benchmark(string name, Predicate<int> predicate)
{
// We could just use new Func<int, bool>(predicate) but that
// would create one delegate wrapping another.
Func<int, bool> func = (Func<int, bool>)
Delegate.CreateDelegate(typeof(Func<int, bool>), predicate.Target,
predicate.Method);
Benchmark("FindAll: " + name, () => ints.FindAll(predicate));
Benchmark("Where: " + name, () => ints.Where(func).Count());
}
static void Benchmark(string name, Action action)
{
GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 50; i++)
{
action();
}
sw.Stop();
Console.WriteLine("{0}: {1}", name, sw.ElapsedMilliseconds);
}
}