Performance of Find() vs. FirstOrDefault()
Similar Question:
Find() vs. Where().FirstOrDefault()
在具有单个字符串属性的简单引用类型的大序列中搜索diana得到了一个有趣的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System; using System.Collections.Generic; using System.Linq; public class Customer{ public string Name {get;set;} } Stopwatch watch = new Stopwatch(); const string diana ="Diana"; while (Console.ReadKey().Key != ConsoleKey.Escape) { //Armour with 1000k++ customers. Wow, should be a product with a great success! :) var customers = (from i in Enumerable.Range(0, 1000000) select new Customer { Name = Guid.NewGuid().ToString() }).ToList(); customers.Insert(999000, new Customer { Name = diana }); // Putting Diana at the end :) //1. System.Linq.Enumerable.DefaultOrFirst() watch.Restart(); customers.FirstOrDefault(c => c.Name == diana); watch.Stop(); Console.WriteLine("Diana was found in {0} ms with System.Linq.Enumerable.FirstOrDefault().", watch.ElapsedMilliseconds); //2. System.Collections.Generic.List<T>.Find() watch.Restart(); customers.Find(c => c.Name == diana); watch.Stop(); Console.WriteLine("Diana was found in {0} ms with System.Collections.Generic.List<T>.Find().", watch.ElapsedMilliseconds); } |
这是因为list.find()中没有枚举器开销,还是这个加上其他的原因?
我能模仿你的结果,所以我对你的程序进行了反编译,在
首先是反编译程序。我把你的数据对象变成了一个你的数据项,只是为了编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | List<\u003C\u003Ef__AnonymousType0<string>> source = Enumerable.ToList(Enumerable.Select(Enumerable.Range(0, 1000000), i => { var local_0 = new { Name = Guid.NewGuid().ToString() }; return local_0; })); source.Insert(999000, new { Name = diana }); stopwatch.Restart(); Enumerable.FirstOrDefault(source, c => c.Name == diana); stopwatch.Stop(); Console.WriteLine("Diana was found in {0} ms with System.Linq.Enumerable.FirstOrDefault().", (object) stopwatch.ElapsedMilliseconds); stopwatch.Restart(); source.Find(c => c.Name == diana); stopwatch.Stop(); Console.WriteLine("Diana was found in {0} ms with System.Collections.Generic.List<T>.Find().", (object) stopwatch.ElapsedMilliseconds); |
这里要注意的关键是,
那么,发现在做什么?这是反编译的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private T[] _items; [__DynamicallyInvokable] public T Find(Predicate<T> match) { if (match == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); for (int index = 0; index < this._size; ++index) { if (match(this._items[index])) return this._items[index]; } return default (T); } |
所以它迭代一个有意义的项数组,因为列表是数组上的包装器。
但是,在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [__DynamicallyInvokable] public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { if (source == null) throw Error.ArgumentNull("source"); if (predicate == null) throw Error.ArgumentNull("predicate"); foreach (TSource source1 in source) { if (predicate(source1)) return source1; } return default (TSource); } |
foreach只是使用可枚举模式的合成糖。看看这张图片
。
我点击foreach查看它在做什么,你可以看到dotpeek想带我去枚举器/当前/下一个有意义的实现。
除此之外,它们基本上是相同的(测试传入的谓词以查看项是否是您想要的)。
我敢打赌,
如果添加第三个测试:
1 2 3 4 5 6 7 8 9 10 | //3. System.Collections.Generic.List<T> foreach Func<Customer, bool> dianaCheck = c => c.Name == diana; watch.Restart(); foreach(var c in customers) { if (dianaCheck(c)) break; } watch.Stop(); Console.WriteLine("Diana was found in {0} ms with System.Collections.Generic.List<T> foreach.", watch.ElapsedMilliseconds); |
它的运行速度与第一个差不多(25毫秒对27毫秒,对于
编辑:如果我添加一个数组循环,它将非常接近于
1 2 3 4 5 6 7 8 9 10 11 | //4. System.Collections.Generic.List<T> for loop var customersArray = customers.ToArray(); watch.Restart(); int customersCount = customersArray.Length; for (int i = 0; i < customersCount; i++) { if (dianaCheck(customers[i])) break; } watch.Stop(); Console.WriteLine("Diana was found in {0} ms with an array for loop.", watch.ElapsedMilliseconds); |
这只比
所以底线是:通过数组元素循环比处理