Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么AddRange比使用foreach循环更快? var fillData=newlist(); 对于(变量i=0;i0) { 此.EnsureCapacity(此._大小+计数); 如果(索引_C#_.net_C# 4.0 - Fatal编程技术网

C# 为什么AddRange比使用foreach循环更快? var fillData=newlist(); 对于(变量i=0;i0) { 此.EnsureCapacity(此._大小+计数); 如果(索引

C# 为什么AddRange比使用foreach循环更快? var fillData=newlist(); 对于(变量i=0;i0) { 此.EnsureCapacity(此._大小+计数); 如果(索引,c#,.net,c#-4.0,C#,.net,C# 4.0,当我从stopwach1和stopwach2获取4结果时,stopwatch1的值总是低于stopwatch2。这意味着addrange总是比foreach快。 有人知道原因吗?因为AddRange检查添加项目的大小,并只增加一次内部数组的大小。如果使用Add,它会根据需要逐渐调整内部数组的大小(加倍),从默认的起始大小10(IIRC)开始。如果您使用: var fillData = new List<int>(); for (var i = 0; i < 100000; i+

当我从
stopwach1
stopwach2
获取4结果时,
stopwatch1
的值总是低于
stopwatch2
。这意味着
addrange
总是比
foreach
快。
有人知道原因吗?

因为
AddRange
检查添加项目的大小,并只增加一次内部数组的大小。

如果使用
Add
,它会根据需要逐渐调整内部数组的大小(加倍),从默认的起始大小10(IIRC)开始。如果您使用:

var fillData = new List<int>();
for (var i = 0; i < 100000; i++)
     fillData.Add(i);

var stopwatch1 = new Stopwatch();
stopwatch1.Start();

var autoFill = new List<int>();
autoFill.AddRange(fillData);
stopwatch1.Stop();

var stopwatch2 = new Stopwatch();
stopwatch2.Start();

var manualFill = new List<int>();

foreach (var i in fillData)
    manualFill.Add(i);
stopwatch2.Stop();

AddRange
可能会检查传递给它的值在哪里实现了
IList
IList
。如果有,它可以找出该范围内有多少个值,因此需要分配多少空间。。。然而,
foreach
循环可能需要多次重新分配

此外,即使在分配之后,
List
也可以用于向基础阵列执行大容量复制(当然,对于实现
IList
的范围)


我怀疑您会发现,如果您再次尝试测试,但对
fillData
使用
Enumerable.Range(0,100000)
而不是
List
,这两个测试将花费大约相同的时间。

使用
AddRange
时,集合可以增加数组的大小一次,然后将值复制到数组中

使用
foreach
语句,集合需要多次增加集合的大小


增加thr size意味着复制整个数组,这需要时间。

AddRange扩展不会遍历每个项,而是将每个项作为一个整体应用。foreach和.AddRange都有其用途。Addrange将赢得针对您当前情况的速度竞赛。

在手动添加项目之前,请尝试初始化初始列表容量:

ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
    int count = is2.Count;
    if (count > 0)
    {
        this.EnsureCapacity(this._size + count);
        // ^^^ this the key bit, and prevents slow growth when possible ^^^
var manualFill=新列表(fillData.Count);

我想这是内存分配优化的结果。 对于AddRange,内存只分配一次,而foreach在每次迭代中都完成了重新分配


在AddRange实现中也可能有一些优化(例如memcpy)

列表AddRange方法的Disassembly from reflector具有以下代码

var manualFill = new List<int>(fillData.Count); 
ICollection is2=收集为ICollection;
如果(is2!=null)
{
int count=is2.count;
如果(计数>0)
{
此.EnsureCapacity(此._大小+计数);
如果(索引<此大小)
{
复制(此._项,索引,此._项,索引+计数,此._大小-索引);
}
if(this==is2)
{
复制(此._项,0,此._项,索引,索引);
数组.Copy(this._items,(int)(索引+计数),this._items,(int)(索引*2),(int)(this._大小-索引));
}
其他的
{
T[]数组=新的T[计数];
is2.CopyTo(数组,0);
数组.CopyTo(此.\u项,索引);
}
这个。_size+=计数;
}
}

正如您所看到的,有一些优化,如EnsureCapacity()调用和使用Array.Copy()。

这就像让服务员十次给您端一瓶啤酒,然后让他一次给您端十瓶啤酒


您认为什么更快:)

这是因为Foreach循环将添加循环每次获得一个值的所有值,
AddRange()方法将收集它作为“块”获取的所有值,并立即将该块添加到指定位置


简单地理解一下,这就像你有一个从市场上带来的10件物品的列表,这将更快地一件一件地或一次性带来所有物品。

如果你担心GC敏感性能(即在一款针对手机的Unity游戏中),AddRange将安排()传入的集合,这是一种分配。增加容量和手动添加可能会更快。
var manualFill = new List<int>(fillData.Count); 
ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
    int count = is2.Count;
    if (count > 0)
    {
        this.EnsureCapacity(this._size + count);
        if (index < this._size)
        {
            Array.Copy(this._items, index, this._items, index + count, this._size - index);
        }
        if (this == is2)
        {
            Array.Copy(this._items, 0, this._items, index, index);
            Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index));
        }
        else
        {
            T[] array = new T[count];
            is2.CopyTo(array, 0);
            array.CopyTo(this._items, index);
        }
        this._size += count;
    }
}