LINQ / IEnumerable Skip()。Take()效率与“yield return”一起使用

时间:2014-02-19 13:41:19

标签: c# performance linq enumerable

Skip()一起使用时,我对Take()IEnumerable<>的效率有疑问。

我将使用IEnumerable<>返回所有数据列表,并使用'yield return'来防止我必须分配大量内存来传回数据。这非常有效。

然而,在我的过程中稍后我想批量处理这些数据,并一次从我的列表中取出20个条目。我心想啊..啊!这非常适合普查员。

我在Skip()上发现了非常有用的Take()IEnumerable interface方法,但我现在意识到这会导致我的循环每次从头开始重新进行交互。

IEnumerable分页数据的最佳方法是什么?我最好在枚举器上使用MoveFirst()MoveNext()而不是Skip()Take()

我做了一些谷歌搜索,但找不到答案..

有人可以帮忙吗?

我真的很喜欢LINQ上的IEnumerable<>功能,但我必须考虑效率。

2 个答案:

答案 0 :(得分:6)

您可以编写Batch方法将项​​目序列转换为给定大小的批处理序列,这可以在不需要多次迭代源序列的情况下完成,这可以限制内存占用只能一次在内存中保存一个批次的大小:

public static IEnumerable<IEnumerable<T>> Batch<T>(
    this IEnumerable<T> source, int batchSize)
{
    List<T> buffer = new List<T>(batchSize);

    foreach (T item in source)
    {
        buffer.Add(item);

        if (buffer.Count >= batchSize)
        {
            yield return buffer;
            buffer = new List<T>(batchSize);
        }
    }
    if (buffer.Count > 0)
    {
        yield return buffer;
    }
}

答案 1 :(得分:0)

内存和CPU之间总会存在权衡。目前,您通过使用Skip向前移动到页面开头来获取页面的项目,并且每个页面请求上的迭代器块将重新计算这些项目。

但是,您可以通过缓存到目前为止计算的项目来避免重新计算,但这将使用一些内存。您声明您决定使用迭代器块以避免使用太多内存,但也许只能缓存必要项的“智能”解决方案可能有用吗?

在Stack Overflow问题Is there an IEnumerable implementation that only iterates over it's source (e.g. LINQ) once的答案中,您会发现一些解决方案只能计算和存储足够的元素才能移动到您的页面。例如。如果您的页面大小为10而您想要第5页,则只会计算并存储前60个项目。第3页的后续请求将使用已经计算的项目,而第10页的请求将计算并缓存足够的项目以获取该页面的数据。

如果要在不从第一个元素开始的情况下执行分页,并且没有不必要的存储未使用的项目,则需要某种方式在特定页面重新启动迭代,而不必迭代所有先前的元素。 IEnumerable<T>IEnumerator<T>无法提供足够的功能。