C#列表<;T>;。今天的表演不好吗?

C#列表<;T>;。今天的表演不好吗?,c#,.net,performance,memory,C#,.net,Performance,Memory,我正在使用.NET3.5(C#),我听说过C#List的性能。ToArray是“坏的”,因为它会复制所有元素的内存以形成一个新数组。这是真的吗?它会在数组中创建新引用,但这只是该方法能够而且应该做的唯一事情…调用ToArray()的原因 如果返回的值不是要修改的,那么将其作为数组返回会使事实更加清楚 如果期望调用方对数据执行许多非顺序访问,那么数组的性能优于列表 如果您知道需要将返回值传递给需要数组的第三方函数 与需要使用.NET版本1或1.1的调用函数的兼容性。这些版本没有列表类型(或者任何

我正在使用.NET3.5(C#),我听说过C#
List的性能。ToArray
是“坏的”,因为它会复制所有元素的内存以形成一个新数组。这是真的吗?

它会在数组中创建新引用,但这只是该方法能够而且应该做的唯一事情…

调用ToArray()的原因

  • 如果返回的值不是要修改的,那么将其作为数组返回会使事实更加清楚
  • 如果期望调用方对数据执行许多非顺序访问,那么数组的性能优于列表
  • 如果您知道需要将返回值传递给需要数组的第三方函数
  • 与需要使用.NET版本1或1.1的调用函数的兼容性。这些版本没有列表类型(或者任何泛型类型)
不打电话给ToArray()的原因

  • 如果调用方确实需要添加或删除元素,则绝对需要一个列表
  • 性能优势不一定得到保证,特别是当调用方以顺序方式访问数据时。另外还有一个从列表到数组的转换步骤,这需要处理时间
  • 调用方始终可以自己将列表转换为数组

摘自

不,那不是真的。性能很好,因为它所做的只是内存复制所有元素(*)以形成一个新数组

当然,这取决于您对“好”或“坏”性能的定义

(*)引用类型的引用,值类型的值

编辑

作为对您评论的回应,使用Reflector是检查实现的一种好方法(见下文)。或者只是想几分钟你将如何实现它,相信微软的工程师不会想出更糟糕的解决方案

public T[] ToArray()
{
    T[] destinationArray = new T[this._size];
    Array.Copy(this._items, 0, destinationArray, 0, this._size);
    return destinationArray;
}
当然,“好的”或“坏的”表现只是相对于一些选择而言的。如果在你的特定情况下,有另一种技术来实现你的目标,这是可测量得更快的,那么你可以认为性能是“坏的”。如果没有这样的替代方案,那么性能就是“好”(或“足够好”)

编辑2

针对“不重新构建对象”的评论:


引用类型没有重建。对于值类型,复制值,这可以粗略地描述为重构。

性能必须以相对术语来理解。将数组转换为列表需要复制数组,复制的成本取决于数组的大小。但您必须将该成本与您的程序正在做的其他事情进行比较。您首先是如何获得要放入阵列的信息的?如果是通过从磁盘、网络连接或数据库中读取,那么内存中的阵列副本不太可能对所用时间产生可检测的影响。

是的,它确实对所有元素进行了内存副本。这是性能问题吗?这取决于您的性能要求

列表
内部包含一个数组,用于保存所有元素。如果容量不再足以容纳列表,则阵列将增长。任何时候,列表都会将所有元素复制到一个新数组中。这种情况经常发生,对大多数人来说,这不是性能问题

例如,具有默认构造函数的列表从容量16开始,当您
.Add()
第17个元素时,它将创建一个大小为32的新数组,复制16个旧值并添加第17个


大小差异也是
ToArray()
返回新数组实例而不是传递私有引用的原因

对于知道长度的任何类型的List/i集合,它都可以从一开始就分配一个大小完全正确的数组

T[] destinationArray = new T[this._size];
Array.Copy(this._items, 0, destinationArray, 0, this._size);
return destinationArray;
如果源类型为IEnumerable(不是列表/集合),则源为:

items = new TElement[4];
..
if (no more space) {
    TElement[] newItems = new TElement[checked(count * 2)];
    Array.Copy(items, 0, newItems, 0, count);
    items = newItems;
它从4号开始,呈指数增长,每次用完空间都会翻倍。每次翻倍时,它都必须重新分配内存并复制数据

如果我们知道源数据的大小,就可以避免这种轻微的开销。然而,在大多数情况下,例如数组大小这是微软关于List.ToArray的时间复杂性的说法

元素是使用Array.Copy复制的,这是一个O(n)操作,其中n是Count

然后,我们看到它通常不是克隆数据,而是使用引用:

如果sourceArray和destinationArray都是引用类型数组或都是Object类型的数组,则执行浅复制。数组的浅层副本是一个新数组,其中包含对与原始数组相同元素的引用。不会复制图元本身或图元引用的任何内容。相反,数组的深度副本复制元素以及元素直接或间接引用的所有内容


总之,这是一种从列表中获取数组的非常有效的方法。

你的意思是只复制引用?很好的引用,但不能直接回答我的问题?你对我的问题的答案是什么?这是我们唯一能给出的答案:正确总是胜过表现。你没有尽你所能做到最好的事情,那仍然是正确的。这一点的应用是,除非必须调用,否则不调用.ToArray()“…数组可能比列表更有性能优势。”-有证据证明这一点吗?对我来说这听起来像个神话。返回数组并不表示它不能被修改。BCL中充满了返回数组的方法,接收者可以自由修改数组。2.0之前的NET framework有非泛型集合和数组。谢谢Joe,你的答案太棒了!您是否有任何相关文件需要进一步讨论或证明“它所做的只是内存复制所有元素(*)以形成一个新的