从List<T>中查找元素,Find与FirstOrDefault孰优孰劣?

image.png

上面这张图片,是今天波波同学发给我的。刚看到图片内容的瞬间,我不禁一惊。因为,在我写的代码中FindFirstOrDefault都曾用到,并且在我的概念里两者几乎都是等价的,并没有本质上的区别。今天看人家还有提供测试数据,似乎我应该将FirstOrDefault全部都换成Find才是最优的解。

仔细回想了一下,很久之前也曾经在StackOverFlow的一个帖子上读到过,类似的讨论。其结论也是Find表现出的性能,会大大优于FirstOrDefault。我把原帖搜索出来了:

https://stackoverflow.com/questions/14032709/performance-of-find-vs-firstordefault

发现这竟然是10多年前的帖子!拜托,那时候还是.NET Framework的天下,连.NET Core 1.0都还没有呢!今天已经就快步入.NET9的时代。这个结论还成立吗?

既然有所怀疑,不如来个实实在在的测试。论证一下,在今天FirstOrDefaultFind是否还存在如此大的差距。

编写测试代码

首先,创建一个.NET8的控制台项目。

dotnet new console -o TestListApp

接着,nuget引入性能测试框架Benchmark.NET

dotnet add package BenchmarkDotNet

最后开始写代码进行测试:

  • 新建一个 ListTest.cs 类文件,然后编写基准测试代码。
[MemoryDiagnoser]
public class ListTest
{
    [Benchmark]
    public int? TestFindInt()
    {
        var list = new List<int>(5000);
        for (int i = 0; i < 5000; i++)
        {
            list.Add(i);
        }
    
        return list.Find(i => i > 1200);
    }
    
    [Benchmark]
    public int? TestFirstInt()
    {
        var list = new List<int>(5000);
        for (int i = 0; i < 5000; i++)
        {
            list.Add(i);
        }
    
        return list.FirstOrDefault(i => i > 1200);
    }
    
    [Benchmark]
    public object? TestFindObject()
    {
        var list = new List<Student>(5000);
        for (int i = 0; i < 5000; i++)
        {
            list.Add(new Student()
            {
                Id = i + 1,
                Name = "Student" + i,
                Age = i % 4 + 18
            });
        }
    
        return list.Find(o => o.Id == 339);
    }
    
    [Benchmark]
    public object? TestFirstObject()
    {
        var list = new List<Student>(5000);
        for (int i = 0; i < 5000; i++)
        {
            list.Add(new Student()
            {
                Id = i+1,
                Name="Student"+i,
                Age = i%4 + 18
            });
        }
    
        return list.FirstOrDefault(o => o.Id==339);
    }
}

class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}
  • 打开 Program.cs 文件,添加以下代码:
internal class Program
{
    var summary = BenchmarkRunner.Run<ListTest>();  
    Console.WriteLine(summary);
}

结果

运行基准测试,几分钟后得到结果如图:

image.png

从结果来看Find确实比FirstOrDefault执行效率略高一些,如果代码里的选择是用Find似乎还是比较明智的。然而,两个方法之间性能差异数值并不算很大,大约在3-10%之间。

因此,我的结论是能用Find尽量用Find,但如果代码已经用了FirstOrDefault也不是什么灾难性的问题,所以不用恐慌,也不必急于马上改过来。淡定自若就可以了。

最后,我还顺带介绍了BenchmarkDotNet作为性能测试框架的使用方法。这也是个一个知识点哦,希望小伙伴们都能Get到~

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容