我有9 GB的数据,我只想要10行。当我这样做时:
data.OrderBy(datum => datum.Column1)
.Take(10)
.ToArray();
我得到OutOfMemoryException
。我想使用OrderByAndTake
方法,针对较低的内存消耗进行了优化。这很容易写,但我想有人已经这样做了。我在哪里可以找到它。
编辑:这是Linq-to-objects。数据来自文件。如果Column1
的值小于10个最大值的当前列表,则可以丢弃每一行。
答案 0 :(得分:2)
它表示:OrderBy是一个Sort,需要存储所有元素(延迟执行被取消)。
当data
是IQueryable时,它应该有效地工作,然后由数据库决定。
// just 4 fun
public static IEnumerable<T> TakeDistinctMin<T, TKey>(this IEnumerable<T> @this,
int n, Func<T, TKey> selector)
where TKey: IComparable<TKey>
{
var tops = new SortedList<TKey, T>(n+1);
foreach (var item in @this)
{
TKey k = selector(item);
if (tops.ContainsKey(k))
continue;
if (tops.Count < n)
{
tops.Add(k, item);
}
else if (k.CompareTo(tops.Keys[tops.Count - 1]) < 0)
{
tops.Add(k, item);
tops.RemoveAt(n);
}
}
return tops.Values;
}
答案 1 :(得分:2)
我假设你在Linq to Objects中这样做了。你可以做点像......
var best = data
.Aggregate(new List<T>(), (soFar, current) => soFar
.Concat(new [] { current })
.OrderBy(datum => datum.Column1)
.Take(10)
.ToList());
通过这种方式,并非所有项目都需要保存在新的排序集合中,而只需要保留最感兴趣的10个项目。
这是最少的代码方式。由于您知道soFar
列表已排序,因此可以优化测试插入current
的位置/时间。我不想为你做所有的工作。 ; - )
PS:将T
替换为您的任何类型。
编辑:想一想,最有效的方式实际上是一个普通的foreach
,它将每个项目与最佳10个项目的运行列表进行比较。
答案 2 :(得分:1)
要订购一组无序对象,你必须查看所有这些对象,不是吗?
我不知道你怎么能够避免解析所有9 GB的数据以获得前10个以某种方式排序的数据,除非已经以这种方式订购了9 GB的数据或者是否有索引或可以使用的其他辅助数据结构。
您能否就问题提供更多背景信息。您是使用LINQ to SQL或Entity Framework还是其他一些O / RM查询数据库?
答案 3 :(得分:1)
您可以将此类内容与projection comparer:
一起使用public static IEnumerable<T> OrderAndTake<T>(this IEnumerable<T> seq,int count,IComparer<T> comp)
{
var resultSet=new SortedSet<T>(comp);
foreach(T elem in seq)
{
resultSet.Add(elem);
if(resultSet.Count>count)
resultSet.Remove(resultSet.Max);
}
return resultSet.Select(x=>x);
}
运行时应为O(log(count)*seq.Count())
和空格O(min(log(count),seq.Count()))
一个问题是,如果你有两个comp.Compare(a,b)==0
元素,它将会中断,因为该集合不允许重复条目。