内存优化OrderBy和Take?

时间:2011-05-20 18:42:36

标签: c# .net linq memory out-of-memory

我有9 GB的数据,我只想要10行。当我这样做时:

 data.OrderBy(datum => datum.Column1)
     .Take(10)
     .ToArray();

我得到OutOfMemoryException。我想使用OrderByAndTake方法,针对较低的内存消耗进行了优化。这很容易写,但我想有人已经这样做了。我在哪里可以找到它。

编辑:这是Linq-to-objects。数据来自文件。如果Column1的值小于10个最大值的当前列表,则可以丢弃每一行。

4 个答案:

答案 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元素,它将会中断,因为该集合不允许重复条目。