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

时间:2024-09-17 00:02   作者:ChenReal    阅读:40

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到~

 

评论
0/200