Linq to Objects 中的 Skip 和 Take 性能

问题描述 投票:0回答:3

“搜索“跳过”和“获取”功能的替代功能”

链接的

1 表示“每次调用 Skip() 时,它都必须从头开始迭代集合,以便跳过所需数量的元素,这会在循环中产生循环(n2 行为)”

结论:对于大型集合,不要使用 Skip 和 Take。寻找另一种方法来迭代您的集合并对其进行划分。

为了访问庞大集合中的最后一页数据,您能否建议我们使用“Skip and Take”方法之外的其他方法?

c# performance linq pagination
3个回答
2
投票

查看Skip

,您可以看到它枚举了所有的项目,甚至是您想要跳过的前n个项目。
但这很奇怪,因为一些 LINQ 方法对集合进行了优化,例如
Count
Last

Skip
显然不是。

如果你有一个数组或

IList<T>
,你可以使用索引器来真正跳过它们:

for (int i = skipStartIndex; i < list.Count; i++) {
    yield return list[i];
}

1
投票

内部确实是正确的:

private static IEnumerable<TSource> SkipIterator<TSource>(IEnumerable<TSource> source, int count)
{
  using (IEnumerator<TSource> enumerator = source.GetEnumerator())
  {
    while (count > 0 && enumerator.MoveNext())
      --count;
    if (count <= 0)
    {
      while (enumerator.MoveNext())
        yield return enumerator.Current;
    }
  }
}

如果您想跳过

IEnumerable<T>
,那么它就可以了。除了枚举之外没有其他方法来获取特定元素。但是您可以在
IReadOnlyList<T>
IList<T>
上编写自己的扩展方法(如果此接口在用于您的元素的集合中实现)。

public static class IReadOnlyListExtensions
{
    public static IEnumerable<T> Skip<T>(this IReadOnlyList<T> collection, int count)
    {
        if (collection == null)
            return null;

        return ICollectionExtensions.YieldSkip(collection, count);
    }

    private static IEnumerable<T> YieldSkip<T>(IReadOnlyList<T> collection, int count)
    {
        for (int index = count; index < collection.Count; index++)
        {
            yield return collection[index];
        }
    }
}

此外,您可以实现它

IEnumerable<T>
,但检查内部是否优化:

if (collection is IReadOnlyList<T>)
{
    // do optimized skip
}

这种解决方案在 Linq 源代码中大量使用(不幸的是,Skip 中没有使用)。


0
投票

取决于您的实现,但使用索引数组来实现此目的是有意义的。

© www.soinside.com 2019 - 2024. All rights reserved.