从一个System.Collections.Generic.List中选择x个元素c#

4
我需要的是一种从列表中选择最后100个元素并以列表形式呈现的方法。
    public List<Model.PIP> GetPIPList()
    {
        if (Repository.PIPRepository.PIPList == null)
            Repository.PIPRepository.Load();
        return Repository.PIPRepository.PIPList.Take(100);

    }

我遇到了这样的错误: “System.Collections.Generic.IEnumerable”无法转换为“System.Collections.Generic.List”。存在显式转换(是否缺少强制类型转换?)
5个回答

11
somelist.Reverse().Take(100).Reverse().ToList();

这将比订购便宜得多:) 同时保留原始订购。

但是这两个反转很耗费资源。 - phoog
@phoog:反转非常便宜。 - leppie
如果somelist的静态类型是List<T>,则你的代码示例将调用List<T>.Reverse()来改变列表。 - phoog
实际上,您的代码示例无法编译,因为 List<T>.Reverse() 返回 void。因此,如果将列表强制转换为 IEnumerable<T>,则会分配两个数组,一个大小与列表相同,另一个大小为列表大小或100个元素中较小的那个。 - phoog
@phoog:你为什么假设somelist是List<T>而不是IEnumerable<T>IQueryable<T>?如果这让你困扰,只需使用somelist.ToEnumerable()即可。 - leppie
显示剩余10条评论

5

如果你的列表很大,最好自己编写代码以获得最佳性能:

public static class ListExtensions
{
    public static IEnumerable<T> LastItems<T>(this IList<T> list, int numberOfItems) //Can also handle arrays
    {
        for (int index = Math.Max(list.Count - numberOfItems, 0); index < list.Count; index++)
            yield return list[index];
    }
}

为什么这比使用Skip()方法更快呢?如果您有一个包含50,000个项目的列表,Skip()方法在开始返回项目之前会调用枚举器的MoveNext()方法49,900次。

为什么它比使用Reverse()方法更快呢?因为Reverse()方法会分配一个足够大的新数组来存储列表的元素,并将它们复制到该数组中。如果数组足够大以至于要进入大对象堆,则特别应避免此操作。


谢谢,你说得对!我最少要处理7000多条数据。 - Taufiq Abdur Rahman

4

编辑:我错过了您说您想要最后100个项目的部分,并且还不能做到。

获取最后100个项目的方法如下:

return Repository.PIPRepository.PIPList
    .OrderByDescending(pip=>pip.??).Take(100)
    .OrderBy(pip=>pip.??);

...然后将您的方法签名更改为返回IEnumerable<Model.PIP>

??表示您将要排序的任何属性。

Joel还提供了一个很好的解决方案,基于计算最后一项中的项目数量,并跳过除100个以外的所有项目。在许多情况下,这可能效果更好。我不想在我的编辑中发布相同的解决方案! :)


@JoelCoehoorn,我实际上完全编辑了我的答案,因为我没有意识到他不知道如何获取最后100个;我卡在了List<>/IEnumerable<>的问题上。 - Andrew Barber

2

尝试:

public List<Model.PIP> GetPIPList()
    {
        if (Repository.PIPRepository.PIPList == null)
            Repository.PIPRepository.Load();
        return Repository.PIPRepository.PIPList.Take(100).ToList();

    }

2
.Take()方法返回的是IEnumerable<T>而不是List<T>。这是一件好事情,您应该强烈考虑改变您的方法和工作习惯,尽可能多地使用IEnumerable<T>
除此之外,.Take(100)也将返回前100个元素,而不是最后100个元素。您需要像这样做:
public IEnumerable<Model.PIP> GetPIPs()
{
    if (Repository.PIPRepository.PIPList == null)
        Repository.PIPRepository.Load();
    return Repository.PIPRepository.PIPList.Skip(Math.Max(0,Repository.PIPRepository.PIPList.Count - 100));
}

如果你真的需要一个列表而不是可枚举对象(提示:你可能不需要),最好仍然使用IEnumerable构建此方法,并在调用此方法的地方使用.ToList()。
将来,您可能需要返回并更新Load()代码以使用IEnumerable,以及后续过程中的代码。这里的终极目标是使对象有效地流式传输到浏览器,并且在Web服务器上一次只加载一个对象。IEnumerable可以实现此目标,而List则不能。

如果“Count”小于“100”,例如有“99”条记录,则会抛出“无效索引异常”。 - Waqas Raja
跳过(skip)和取(take)是否针对列表使用随机访问进行了优化?如果没有,自己编写代码会更好,特别是当你的列表很大时。 - phoog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接