未优化的IEnumerable<> .Last with predicate

时间:2013-12-05 16:32:10

标签: c# .net linq extension-methods ienumerable

public static TSource Last<TSource>(this IEnumerable<TSource> source)的{​​{1}}扩展方法对IEnumerable<TSource>类型的源使用优化,这样当它只能使用索引进行结束时,它不会遍历整个序列

IList<TSource>

我略微修改了代码,使其可读,但功能保持不变。

为什么IList<TSource> tSources = source as IList<TSource>; if (tSources != null) { int count = tSources.Count; if (count > 0) { return tSources[count - 1]; } } 在从序列末尾开始迭代时也不会被优化?

如果应该有与谓词匹配的东西,并且它接近序列的开头,那么我仍然需要迭代到最后。如果在最后有匹配的东西,我不必再迭代,因为我从最后开始。

我希望像这样的东西成为方法的一部分。

public static TSource Last<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

2 个答案:

答案 0 :(得分:2)

我的猜测,这是一个黑暗的镜头,你可能会得到任何答案:在谓词情况下优化会导致更多的副作用。优化通常会导致副作用,因为未调用IEnumarble的GetEnumerator()。但是在谓词的情况下,你可以从外面观察它没有按顺序调用。有人可能会编写一个谓词,希望迭代按顺序发生,而倒退就会破坏它。

答案 1 :(得分:0)

除非IList<T>的实现包含非常少量的项目,否则优化非谓词Last以返回theList[theList.Count()-1]的速度不可能比从一开始就简单枚举慢;对于任何合理的IList<T>实现来说,它不太可能会慢得多。尽管IList<T>的某些实现按索引查找项目所需的时间可能远远长于按顺序获取数据所需的每个项目的平均时间(可能是一个数量级,但可能不是两个),按索引读取一个项目通常比按顺序读取每个项目花费的时间少得多(随机访问通常不会比顺序访问慢太多除非集合很大;如果在一个1,000,000项集合中,按索引读取项目的成本是顺序访问所需的每项目时间的1,000倍[非典型大额惩罚],那仍然是阅读每一个项目。)

Last与谓词一起使用时,无法保证需要检查多少项。如果随机访问比顺序访问(常见)花费更长时间,并且最后一次匹配恰好发生在列表的早期(非常合理),那么从开始处理列表将比从结束处理更快。鉴于IList<T>实现执行随机访问需要十倍的时间来执行顺序提取是不合理的,从后面读取的“优化”最终会使事情变得慢一个数量级而不是按顺序阅读的“未经优化的”代码。

如果IList<T>提供了更多用于读取数据的选项,和/或包含大致描述不同操作的相对成本的属性,那么可以优化Last方法来检查其他一些列表项顺序,但没有这样的功能,这种方法最好使用一种简单的方法,如果没有灵感,那么其行为是可预测的,而不是那种试图聪明但有时可能表现更差的方法。