我需要一个类似于
以下是竞争函数。
TakeLast<T>(int n)
的LINQ函数,涉及编程内容。我找到了这篇StackOverflow文章:https://dev59.com/G3A75IYBdhLWcg3wJFcL#3453282。我喜欢这个答案,因为它实现简单。后来,我的另一个同事指出Reverse()
比Skip(length - n)
更耗费资源。这促使我写了一个测试。以下是竞争函数。
public static IEnumerable<T> TakeLast<T>( this IEnumerable<T> c, int n ) {
return c.Reverse().Take( n ).Reverse();
}
public static IEnumerable<T> TakeLast2<T>( this IEnumerable<T> c, int n ) {
var count = c.Count();
return c.Skip( count - n );
}
我测量了获取枚举类型Enumerable.Range(0, 100000)
中最后10个元素的执行时间。发现:
TakeLast()
更快,速度大约是原来的5倍。- 使用
TakeLast()
枚举时,第二次及以后枚举明显更快。
以下是我的代码的.NET Fiddle(本地运行过,这里也有演示):http://dotnetfiddle.net/ru7PZE
问题
- 为什么
TakeLast()
更快? - 为什么使用
TakeLast()
枚举时,第二次及以后枚举比第一次枚举快得多,但所有对TakeLast2()
的枚举都差不多?
Enumerable.Range
的实现密切相关。如果您真的关心List<T>
的性能,应该使用它进行分析。如果您真的关心Range
的性能,可以使用适当的起始/计数值创建一个新的Range
。 - Tim S.IList
的事实仅意味着Count
不需要迭代序列,它可以免费获取计数,从而消除了生成查询时的时间差异。 这也使Skip
直接跳到正确的位置。 实现IList
的源序列是第二种方法的最佳情况; 第一种方法受益,但没有那么多。 - Servy