【问题标题】:Efficiency of ToList() [duplicate]ToList() 的效率 [重复]
【发布时间】:2018-02-23 10:35:54
【问题描述】:

List 相比,与IEnumerable 相比,与IEnumerable 一起工作的许多开发人员感觉更自在(例如)。我想知道ToList() 过度使用是否会对性能产生任何影响。例如,或者,将在订购后使用ToList() 重新获取列表,即

private void ListThinger(List<T> input)
{
  input = input.OrderBy(s => s.Thing).ToList();
  foreach(var thing in input)
  {
      // do things
  }
}

我的问题是:

  • ToList() 方法的效率如何?假设内容是 POCO,它会创建一个新列表吗?这需要多少内存?如果它是值类型而不是 POCO,这会改变吗?
  • 列表大小决定效率还是列表大小不决定ToList()的成本?
  • 如果一个列表被转换为IEnumerable,然后在其上调用ToList(),它会只返回原始对象吗?

附:我知道单次使用 ToList 不会破坏任何支持,但我们正在构建一个当前受 CPU 限制的高并发系统,所以我正在寻找一些小的胜利,当扩展时,会带来很大的改进

【问题讨论】:

  • 如文档所述,它创建来自IEnumerable&lt;T&gt;List&lt;T&gt;。所以问题1p2和3已经回答了(你的第一个问题实际上是4个问题,应该分开)
  • 在你的具体例子中,ToList 应该被删除,因为它没有增加任何价值(因为你所做的只是一次迭代它)。
  • 你有什么理由不能自己测试这些东西?
  • ToList() 非常高效。不需要时调用它是低效的。

标签: c# .net


【解决方案1】:
  1. 是的,是创建一个新列表。很难准确测量内存使用情况,但很可能是类大小 +(系统字大小 * 元素计数)。我推荐一个内存分析器。
  2. 运算的算法效率当然会受到元素数量的影响
  3. 是的,您每次都会得到一个全新的列表。里面的引用重复,但原语是​​重复的。

自己试试吧:

var list = new List<int>();
bool areListsTheSame = list == ((IEnumerable<int>)list).ToList();

【讨论】:

    【解决方案2】:

    ToList() 方法的效率如何?假设内容是 POCO,它会创建一个新列表吗?这需要多少内存?如果它是值类型而不是 POCO,这会改变吗?

    ToList() 方法通过创建一个新列表并用给定集合的项目填充它来实现给定集合。 Linq.ToList() implementation:

    public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) {
        if (source == null) throw Error.ArgumentNull("source");
        return new List<TSource>(source);
    }
    

    这样做你并没有获得延迟执行(如果需要)的能力


    列表的大小决定效率还是列表的大小不决定 ToList() 的成本?

    当它调用Lists 复制构造函数并创建一个新列表时,它将作用于每个项目。所以它将在O(n) 中运行——这意味着列表的大小很重要。关于复制构造函数操作的 MSDN 文档:

    初始化 List 类的新实例,该实例包含从指定集合复制的元素,并且有足够的容量容纳复制的元素数量。

    正如@Jason 在下面的评论中提到的,Copy Constructor 是智能且高效的,但在不需要时执行它仍然是一个不必发生的 O(n) 操作


    如果一个列表被强制转换为一个 IEnumerable,然后对其调用 ToList(),它会只返回原始对象吗?

    没有。如上所示,它将创建一个新列表


    至于您的示例代码:

    input = input.OrderBy(s => s.Thing).ToList();
    foreach(var thing in input)
    {
       // do things
    }
    

    当您获得具体化列表(而不是可能在延迟执行中执行的 IQueriable/IEnumerable)时,在添加后添加 ToList 不会给您带来任何好处。

    你可以看这里,也可能有帮助:When to use LINQ's .ToList() or .ToArray()

    【讨论】:

    • 可能至少值得注意的是,在构造函数中,如果它可以转换为 ICollection&lt;T&gt;,它会尝试通过使用 Array.CopyTo 来提高效率,虽然仍然是 O(n),但它是由于它使用非托管的内部 Copy 方法,因此比您自己更高效。
    • 不如 .NET 核心中的新 ToArray 东西高效,它创建缓冲区的链接列表,而不是在内部数组用完时执行复制调整大小。它会进行一次完整的迭代,捕获缓冲区集群中的所有元素,然后将它们全部复制到最后的数组中。
    • @AdamHouldsworth 和OrderBy 如果源是固定长度的,那么它甚至不需要那个链表,因为它知道开始的大小,(效率也与ToList() 一起使用在这种情况下),但即使是最高效的ToList() 在没有收获的情况下也会比不做效率低。
    • @Chris - 这对你有帮助吗?
    猜你喜欢
    • 1970-01-01
    • 2011-11-09
    • 2011-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-23
    相关资源
    最近更新 更多