我承认我并没有尝试对此进行基准测试,但我很好奇......
Enumerable.ToArray<T>
(及其堂兄Enumerable.ToList<T>
)的CPU /内存特征是什么?
由于IEnumerable
没有预先通告它有多少元素,我(或许天真地)假设ToArray
必须“猜测”一个初始数组大小,然后重新调整/重新分配数组如果第一次猜测看起来太小,那么如果第二次猜测看起来太小等那么又要再次调整它...这会产生比线性更差的性能。
我可以想象更好的方法涉及(混合)列表,但这仍然需要多个分配(虽然不是重新分配)和相当多的复制,尽管它可能是线性整体,尽管有开销。
幕后是否有任何“魔法”发生,这避免了重复调整大小的需要,并使ToArray
在空间和时间上呈线性?
更一般地说,是否有关于BCL性能特征的“官方”文档?
答案 0 :(得分:15)
没有魔力。如果需要,可以调整大小。
请注意,始终不是必需的。如果IEnumerable<T>
.ToArray
也实现了ICollection<T>
,那么.Count
属性用于预分配数组(使算法在空间和时间上呈线性关系。)如果但是,不会执行以下(粗略)代码:
foreach (TElement current in source)
{
if (array == null)
{
array = new TElement[4];
}
else
{
if (array.Length == num)
{
// Doubling happens *here*
TElement[] array2 = new TElement[checked(num * 2)];
Array.Copy(array, 0, array2, 0, num);
array = array2;
}
}
array[num] = current;
num++;
}
注意阵列填充时加倍。
无论如何,除非您绝对需要,否则通常最好避免调用.ToArray()
和.ToList()
。在需要时直接询问查询通常是更好的选择。
答案 1 :(得分:7)
我使用.NET Reflector提取了.ToArray()方法后面的代码:
public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
Buffer<TSource> buffer = new Buffer<TSource>(source);
return buffer.ToArray();
}
和Buffer.ToArray:
internal TElement[] ToArray()
{
if (this.count == 0)
{
return new TElement[0];
}
if (this.items.Length == this.count)
{
return this.items;
}
TElement[] destinationArray = new TElement[this.count];
Array.Copy(this.items, 0, destinationArray, 0, this.count);
return destinationArray;
}
在Buffer构造函数中,它遍历所有元素以计算实数Count和Elements数组。
答案 2 :(得分:2)
请记住,对于大多数类型,您需要存储的所有内容都是引用。这并不像你分配足够的内存来复制整个对象(除非你使用了很多结构...... tsk tsk)。
避免使用.ToArray()或.ToList()直到最后一刻仍然是个好主意。大多数情况下,您可以继续使用IEnumerable&lt; T&gt;一直到你运行foreach循环或将其分配给数据源。