c# - Find() 与 FirstOrDefault() 的性能对比

标签 c# .net performance linq

<分区>

Similar Question:
Find() vs. Where().FirstOrDefault()

在具有单个字符串属性的简单引用类型的大序列中搜索 Diana 时得到了一个有趣的结果。

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);
    }

enter image description here

这是因为 List.Find() 中没有 Enumerator 开销,还是因为这加上其他原因?

Find() 运行速度几乎是原来的两倍,希望 .Net 团队将来不会将其标记为过时。

最佳答案

我能够模仿您的结果,所以我反编译了您的程序,FindFirstOrDefault 之间存在差异。

首先是反编译的程序。我让你的数据对象成为一个匿名数据项,只是为了编译

    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);

这里要注意的关键是 FirstOrDefault 是在 Enumerable 上调用的,而 Find 是作为源列表上的方法调用的。

那么,find 在做什么?这是反编译后的Find方法

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);
}

因此它迭代了一个有意义的项目数组,因为列表是数组的包装器。

但是,FirstOrDefaultEnumerable 类上使用 foreach 来迭代项目。这使用列表的迭代器并移动到下一个。我认为您看到的是迭代器的开销

[__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 就是 syntatic sugar关于使用可枚举模式。看这张图

enter image description here .

我单击 foreach 以查看它在做什么,您可以看到 dotpeek 想要带我到枚举器/当前/下一个实现,这很有意义。

除此之外它们基本相同(测试传入的谓词以查看某个项目是否是您想要的)

关于c# - Find() 与 FirstOrDefault() 的性能对比,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14032709/

相关文章:

.net - 为什么在提供阿拉伯文本时 String.IndexOf 和 String.Contains 不一致?

c# - .NET SDK 中的 Couchbase NodeUnavailableException

Mysql:唯一索引=大型数据集的性能特征?

c# - 编写一个通用方法来替换一系列遗留 API 方法

c# - 为什么我不能在静态类中有实例成员,但我可以在静态方法中有实例成员?

c# - 如何在 VS2010 中禁用此代码 View 设置?

iOS 在哪里放置自定义单元格设计? awakeFromNib 或 cellForRowAtIndexPath?

c - 在c : do internal states improve speed?中

c# - 如何识别带状线集合(轴)(序列化)中的带状线?

c# - DateTime.TryParseExact 将我日期中的月份错误地解析为一月