【发布时间】:2010-01-16 16:20:35
【问题描述】:
从一个非常大的集合中获取前 10 条记录并使用自定义 OrderBy 的好方法是什么?如果我使用 LINQ to Objects OrderBy 方法,它会很慢并且占用大量内存,因为它会使用新顺序创建一个全新的集合。我想要一种新方法,其签名如下,它不会对整个集合重新排序并且速度非常快:
public static IEnumerable<TSource> OrderByTop<TSource, TKey>(
IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
int topCount)
我尝试编写它,但它变得非常复杂,我认为使用 Aggregate 或其他东西可能有更简单的方法。任何帮助将不胜感激。
回答
感谢您的帮助。我最终得到了以下代码:
public static List<TSource> OrderByTop<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
int topCount)
{
var itemComparer = keySelector.ToIComparer(comparer);
return source.Aggregate(
new List<TSource>(topCount),
(List<TSource> list, TSource item) =>
list.SortedInsert(item, itemComparer, topCount));
}
列表扩展方法 SortedInsert 如下:
public static List<T> SortedInsert<T>(
this List<T> list,
T item,
IComparer<T> comparer,
int maxLength)
{
if (list.Count == maxLength)
if (comparer.Compare(item, list[maxLength - 1]) >= 0)
return list;
else
list.RemoveAt(maxLength - 1);
int insertIndex = list.BinarySearch(item, comparer);
if (insertIndex < 0)
insertIndex = ~insertIndex;
list.Insert(insertIndex, item);
return list;
}
对于那些感兴趣的人,我还有 keySelector 扩展方法可以转换为 IComparer。
public static IComparer<TSource> ToIComparer<TSource, TKey>(
this Func<TSource, TKey> keySelector,
IComparer<TKey> comparer)
{
return new KeySelectorToIComparerConverter<TSource, TKey>(
keySelector,
comparer);
}
private class KeySelectorToIComparerConverter<TSource, TKey>
: IComparer<TSource>
{
private readonly IComparer<TKey> comparer;
private readonly Func<TSource, TKey> keySelector;
public KeySelectorToIComparerConverter(
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer)
{
this.comparer = comparer;
this.keySelector = keySelector;
}
public int Compare(TSource x, TSource y)
{
return comparer.Compare(keySelector(x), keySelector(y));
}
}
【问题讨论】:
-
库MoreLinq 中有一个方法PartialSort 可以满足您的要求。 implementation 在
List<T>上使用BinarySearch来保存顶部元素。
标签: c# linq performance linq-to-objects sql-order-by