我非常喜欢Last()
并且会一直使用List<T>
s。但由于它似乎是为IEnumerable<T>
定义的,我猜它首先枚举枚举 - 这应该是O(n)而不是O(1)直接索引List<T>
的最后一个元素。
标准(Linq)扩展方法是否意识到这一点?
C ++中的STL通过迭代器和其他东西的整个“继承树”来意识到这一点。
答案 0 :(得分:39)
我刚刚使用Reference Source查看Last的代码,它首先检查它是否是IList<T>
并执行相应的O(1)调用:
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();
}
所以你有一个演员阵容的轻微开销,但不是枚举的巨大开销。
答案 1 :(得分:22)
您可以将{Last List<T>
与Last一起使用,而无需担心:)
Enumerable.Last尝试将IEnumerable<T>
实例向下转换为IList<T>
。如果可以,则使用索引器和Count属性。
以下是Reflector看到的实施的一部分:
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
int count = list.Count;
if (count > 0)
{
return list[count - 1];
}
}
答案 2 :(得分:7)
它包含对实现IList<T>
的任何内容的优化,在这种情况下,它只是查找项目的长度为-1。
请注意,您发送的绝大多数内容都会实现IList<T>
List<int>
int[]
依此类推...全部实施IList<T>
对于那些无法查看代码进行确认的人,可以使用观察确认:
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();
}
}
}
输出:
Raw performance Time Elapsed 1 ms With Last Time Elapsed 31 ms
所以它只慢了30倍并且保持了你所拥有的任何长度列表的性能配置文件,这在大的方案中没什么。
答案 3 :(得分:2)
对于List<T>
,它是O(1),但对于其他可枚举,它可能是O(N)。
答案 4 :(得分:0)
简短回答:
O(1)。
<强>解释强>:
很明显 List 的 Last()使用 Count()扩展方法。
Count()在运行时检查集合的类型,并使用 Count 属性(如果可用)。
列表的Count 属性具有O(1)复杂度,因此是 Last()扩展方法。