What is the performance of the Last() extension method for List<T>?

冷暖自知 提交于 2019-11-26 17:33:23

问题


I really like Last() and would use it all the time for List<T>s. But since it seems to be defined for IEnumerable<T>, I guess it enumerates the enumeration first - this should be O(n) as opposed to O(1) for directly indexing the last element of a List<T>.

Are the standard (Linq) extension methods aware of this?

The STL in C++ is aware of this by virtue of a whole "inheritance tree" for iterators and whatnot.


回答1:


I just used the Reference Source to look into the code for Last and it checks to see if it is a IList<T> first and performs the appropriate O(1) call:

public static TSource Last < TSource > (this IEnumerable < TSource > source) {
    if (source == null) throw Error.ArgumentNull("source");
    IList < TSource > list = source as IList < TSource > ;
    if (list != null) {
        int count = list.Count;
        if (count > 0) return list[count - 1];
    }
    else {
        using(IEnumerator < TSource > e = source.GetEnumerator()) {
            if (e.MoveNext()) {
                TSource result;
                do {
                    result = e.Current;
                } while ( e . MoveNext ());
                return result;
            }
        }
    }
    throw Error.NoElements();
}

So you have the slight overhead of a cast, but not the huge overhead of enumerating.




回答2:


You can just use Last with List<T> without worrying :)

Enumerable.Last attempts to downcast the IEnumerable<T> instance to IList<T> . If this is possible, it uses the indexer and Count property.

Here is part of the implementation as Reflector sees it:

IList<TSource> list = source as IList<TSource>;
if (list != null)
{
    int count = list.Count;
    if (count > 0)
    {
        return list[count - 1];
    }
}



回答3:


It contains an optimisation for anything that implements IList<T> in which case it just looks up the item at length -1.

Keep in mind that the vast majority of stuff you will send in will implement IList<T>

List<int> 
int[] 

and so on ... all implement IList<T>

For those who can not look at the code to confirm, you can confirm it using observation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication4 {
    class Program {

        static void Profile(string description, int iterations, Action func) {

            // clean up
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            // warm up 
            func();

            var watch = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++) {
                func();
            }
            watch.Stop();
            Console.Write(description);
            Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
        }

        static void Main(string[] args) {
            int[] nums = Enumerable.Range(1, 1000000).ToArray();

            int a;

            Profile("Raw performance", 100000, () => { a = nums[nums.Length - 1];  });
            Profile("With Last", 100000, () => { a = nums.Last(); }); 

            Console.ReadKey();
        }


    }
}

Output:

Raw performance Time Elapsed 1 ms
With Last Time Elapsed 31 ms

So it's only 30 times slower and maintains that performance profile with whatever length list you have, which is nothing in the big scheme of things.




回答4:


For List<T> it is O(1), but for other enumerables it may be O(N).




回答5:


Short answer:

O(1).

Explanation:

It's evident that Last() for List uses Count() extension method.

Count() checks type of the collection in runtime and uses Count property if it's available.

Count property for list has O(1) complexity so is the Last() extension method.



来源:https://stackoverflow.com/questions/1377864/what-is-the-performance-of-the-last-extension-method-for-listt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!