2012-03-23 117 views
52
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(); 

當我從stopwach1stopwach2 結果,stopwatch1具有比stopwatch2總是較低的值。這意味着addrange總是比foreach更快。 有誰知道爲什麼?爲什麼AddRange比使用foreach循環更快?

回答

76

潛在地,AddRange可以檢查其中傳遞給它的值實現IListIList<T>。如果確實如此,它可以找出該範圍內有多少個值,因此需要分配多少空間...而foreach循環可能需要重新分配多次。

另外,即使分配後,List<T>可以使用IList<T>.CopyTo執行批量複製到下面的數組(它們實現IList<T>,當然範圍。)

我懷疑你會發現,如果你試圖測試但是使用Enumerable.Range(0, 100000)代替fillData而不是List<T>,兩者將大致相同。

15

因爲AddRange會檢查添加項目的大小,並只增加一次內部數組的大小。

55

如果您使用的是Add,則它將根據需要(加倍)從默認開始大小10(IIRC)逐漸調整內部陣列的大小。如果你使用:

var manualFill = new List<int>(fillData.Count); 

我希望它會發生根本性的變化(不再調整大小/數據副本)。

從反射器,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 ^^^ 
+3

我總是用'反射器'代碼來回答問題。 ** + 1 ** – gdoron 2012-03-25 07:23:20

4

使用AddRange時,Collection可以增加一次數組的大小,然後將值複製到其中。

使用foreach聲明集合需要不止一次地增加集合的大小。

增加thr大小意味着複製需要時間的完整數組。

0

AddRange擴展不會遍歷每個項目,而是將每個項目作爲一個整體應用。 foreach和.AddRange都有一個目的。 Addrange將爲您目前的狀況贏得速度比賽。

更多在這裏:

Addrange Vs Foreach

1

嘗試之前手動添加該項目從初始化intiial列表容量:

var manualFill = new List<int>(fillData.Count); 
2

我想這是內存分配優化的結果。 AddRange內存的 只分配一次,並在每次迭代重新分配時完成。

也可以是有在的AddRange執行一些最佳化(的memcpy例如)

6

的dissassembly從反射器的列表的AddRange方法具有下面的代碼

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; 
    } 
} 

正如你可以看到有一些如EnsureCapacity()調用和使用Array.Copy()的優化。

4

這就像是要求服務員給你帶來一瓶啤酒十次,並要求他一次帶上10杯啤酒。

你認爲什麼是快:)

1

這是因爲foreach循環將增加所有環路越來越之一的時間和
的的AddRange()方法將齊聚它是所有值的值作爲一個「塊」並立即將該塊添加到指定的位置。

簡單的瞭解,就像你有一個從市場上帶來的10件物品的清單,這將更快地將所有這一切,一個接一個或所有在一次。

相關問題