從List<T>中查找元素,F(xiàn)ind與FirstOrDefault孰優(yōu)孰劣?

image.png

上面這張圖片,是今天波波同學(xué)發(fā)給我的。剛看到圖片內(nèi)容的瞬間,我不禁一驚。因?yàn)?,在我寫的代碼中FindFirstOrDefault都曾用到,并且在我的概念里兩者幾乎都是等價(jià)的,并沒(méi)有本質(zhì)上的區(qū)別。今天看人家還有提供測(cè)試數(shù)據(jù),似乎我應(yīng)該將FirstOrDefault全部都換成Find才是最優(yōu)的解。

仔細(xì)回想了一下,很久之前也曾經(jīng)在StackOverFlow的一個(gè)帖子上讀到過(guò),類似的討論。其結(jié)論也是Find表現(xiàn)出的性能,會(huì)大大優(yōu)于FirstOrDefault。我把原帖搜索出來(lái)了:

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

發(fā)現(xiàn)這竟然是10多年前的帖子!拜托,那時(shí)候還是.NET Framework的天下,連.NET Core 1.0都還沒(méi)有呢!今天已經(jīng)就快步入.NET9的時(shí)代。這個(gè)結(jié)論還成立嗎?

既然有所懷疑,不如來(lái)個(gè)實(shí)實(shí)在在的測(cè)試。論證一下,在今天FirstOrDefaultFind是否還存在如此大的差距。

編寫測(cè)試代碼

首先,創(chuàng)建一個(gè).NET8的控制臺(tái)項(xiàng)目。

dotnet new console -o TestListApp

接著,nuget引入性能測(cè)試框架Benchmark.NET。

dotnet add package BenchmarkDotNet

最后開(kāi)始寫代碼進(jìn)行測(cè)試:

  • 新建一個(gè) ListTest.cs 類文件,然后編寫基準(zhǔn)測(cè)試代碼。
[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; }
}
  • 打開(kāi) Program.cs 文件,添加以下代碼:
internal class Program
{
    var summary = BenchmarkRunner.Run<ListTest>();  
    Console.WriteLine(summary);
}

結(jié)果

運(yùn)行基準(zhǔn)測(cè)試,幾分鐘后得到結(jié)果如圖:

image.png

從結(jié)果來(lái)看Find確實(shí)比FirstOrDefault執(zhí)行效率略高一些,如果代碼里的選擇是用Find似乎還是比較明智的。然而,兩個(gè)方法之間性能差異數(shù)值并不算很大,大約在3-10%之間。

因此,我的結(jié)論是能用Find盡量用Find,但如果代碼已經(jīng)用了FirstOrDefault也不是什么災(zāi)難性的問(wèn)題,所以不用恐慌,也不必急于馬上改過(guò)來(lái)。淡定自若就可以了。

最后,我還順帶介紹了BenchmarkDotNet作為性能測(cè)試框架的使用方法。這也是個(gè)一個(gè)知識(shí)點(diǎn)哦,希望小伙伴們都能Get到~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容