LINQ實戰技巧 上課筆記 - 有無使用ToList()差異

最近參加了SkillTree的課程,不想讓這些學習效果白白浪費,所以決定把這幾天所學到的東西寫下來。

LINQ 並不會讓程式變快 只會讓你寫程式變快

基本上效能的表現關係如右: For>=Foreach>Where

  1. For 及 Foreach基本上無太大差異(Compiler會把Foreach最佳化成For)
  2. For可用在非Enumerble的元素集; Foreach用在Enumerble的元素集會更好懂 
    1. Enumerable說明:http://vegeee-csharp.blogspot.com/2017/02/c-ienumerableienumerator-ienumerable.html
  3. Where會變慢原因: Delegate的呼叫、使用者寫法問題

LINQ常見的誤用

  1. LINQ大約只有一半的函示延遲值執行(Deferred Execution: https://dotblogs.com.tw/hatelove/2013/09/10/csharp-linq-deferred-execution-introduction)
  2. 另一半則是即刻查詢的函示(要小心)
    1. LastOrDefault: Traverse至最後一個元素(等同於Traverse全部)
    2. OrderBy, Reverse: 針對所有元素做處理
    3. ToList, ToLookup, ToDictionary: 針對所查詢的元素們做複製的動作
    4. All, Min, Max, Sum, Count: 所有元素都必須參與比較

LINQ不必要的查詢

        public void MultipleQuery()

        {

            var sw = new Stopwatch();

            var data = Enumerable.Range(1, 5000000).ToList();

            data.Add(5000003);

            sw.Start();

            var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003);

            var target = filters.FirstOrDefault(a => a == 5000001);

            if (target == 0)

                target = filters.FirstOrDefault(a => a == 5000002);

            if (target == 0)

                target = filters.FirstOrDefault(a => a == 5000003);

            sw.Stop();

            Console.WriteLine("Without ToList:" + sw.ElapsedMilliseconds + "(ms)");

        }



        public void MultipleQuery2()

        {

            var sw = new Stopwatch();

            var data = Enumerable.Range(1, 5000000).ToList();

            data.Add(5000003);

            sw.Start();

            var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003).ToList();

            var target = filters.FirstOrDefault(a => a == 5000001);

            if (target == 0)

                target = filters.FirstOrDefault(a => a == 5000002);

            if (target == 0)

                target = filters.FirstOrDefault(a => a == 5000003);

            sw.Stop();

            Console.WriteLine("With ToList:" + sw.ElapsedMilliseconds+"(ms)");

        }

執行結果秒數

Without ToList:93(ms)

With ToList:40(ms)

Without ToList:94(ms)

With ToList:30(ms)

Without ToList:83(ms)

With ToList:32(ms)

MultiQuery

var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003);

MultiQuery2

var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003).ToList();

可以看到其實只差在有沒有在結尾加上ToList()。

會造成這樣的原因就是我們上面所提到的ToList()是屬於即刻查詢的一種

當你加上他的時候此時在filters內的就只有{5000001, 5000002, 5000003}

等到後面要執行

var target = filters.FirstOrDefault(a => a == 5000001);

我們只需要針對{5000001, 5000002, 5000003}去做where即可,大大地縮小我們所需要查詢的空間大小。

 

那沒有加上ToList()是怎麼回事呢? 相信你也猜到了,因為這個運算是屬於延遲執行的。

所以當程式碼執行到

var filters = data.Where(a => a == 5000001 || a == 5000002 || a == 5000003);

是不會馬上執行的,而是當我們執行到

var target = filters.FirstOrDefault(a => a == 5000001);

才會真正的對(a => a == 5000001 || a == 5000002 || a == 5000003)這個條件去做篩選,加上我們刻意把目標放在最後才讓程式碼找到,整整多了三次執行這個查詢條件,這也是造成效能大輸的原因阿~

 

今天所學的重點就是

  1. 必須明確分辨即刻查詢及延遲執行的差異
  2. 排序及搜尋還是要善用演算法,畢竟LINQ並不是以效能著名的工具
  3. 記得我們是讓寫程式變快,不是讓程式變快

 

 

PS: 本人只是一個新手,若有謬誤,煩請指教,謝謝。