2013-04-09 77 views
2

這些枚舉之一是否比另一個快?或者差不多? (在C#示例)哪個.NET集合更快:枚舉foreach Dictionary <>。Values或List <>?

情況1:

Dictionary<string, object> valuesDict; 

// valuesDict loaded with thousands of objects 

foreach (object value in valuesDict.Values) { /* process */ } 

情況2:

List<object> valuesList; 

// valuesList loaded with thousands of objects 

foreach (object value in valuesList) { /* process */ } 

UPDATE:

背景:

字典將是鍵控搜索有益別處(而不是迭代一個列表),但如果迭代,益處會減少通過字典比通過列表慢得多。

更新: 聽取了很多人的意見,我做了我自己的測試。

首先,這些是結果。以下是該計劃。

迭代整個集合 快譯通:78 Keyd:131 名單:76

鍵控搜索集合 快譯通:178 Keyd:194 列表:142800

using System; 
using System.Linq; 

namespace IterateCollections 
{ 
    public class Data 
    { 
     public string Id; 
     public string Text; 
    } 

    public class KeyedData : System.Collections.ObjectModel.KeyedCollection<string, Data> 
    { 
     protected override string GetKeyForItem(Data item) 
     { 
      return item.Id; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var dict = new System.Collections.Generic.Dictionary<string, Data>(); 
      var list = new System.Collections.Generic.List<Data>(); 
      var keyd = new KeyedData(); 

      for (int i = 0; i < 10000; i++) 
      { 
       string s = i.ToString(); 
       var d = new Data { Id = s, Text = s }; 
       dict.Add(d.Id, d); 
       list.Add(d); 
       keyd.Add(d); 
      } 

      var sw = new System.Diagnostics.Stopwatch(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       foreach (Data d in dict.Values) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      var dictTime = sw.ElapsedMilliseconds; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       foreach (Data d in keyd) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      var keydTime = sw.ElapsedMilliseconds; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       foreach (Data d in list) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      var listTime = sw.ElapsedMilliseconds; 

      Console.WriteLine("Iterate whole collection"); 
      Console.WriteLine("Dict: " + dictTime); 
      Console.WriteLine("Keyd: " + keydTime); 
      Console.WriteLine("List: " + listTime); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       for (int i = 0; i < 10000; i += 10) 
       { 
        string s = i.ToString(); 
        Data d = dict[s]; 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      dictTime = sw.ElapsedMilliseconds; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       for (int i = 0; i < 10000; i += 10) 
       { 
        string s = i.ToString(); 
        Data d = keyd[s]; 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      keydTime = sw.ElapsedMilliseconds; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 10; r++) 
      { 
       for (int i = 0; i < 10000; i += 10) 
       { 
        string s = i.ToString(); 
        Data d = list.FirstOrDefault(item => item.Id == s); 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      listTime = sw.ElapsedMilliseconds * 100; 

      Console.WriteLine("Keyed search collection"); 
      Console.WriteLine("Dict: " + dictTime); 
      Console.WriteLine("Keyd: " + keydTime); 
      Console.WriteLine("List: " + listTime); 

     } 
    } 

}

更新:

按照@Blam建議的字典與KeyedCollection的比較。

最快的方法是遍歷一個KeyedCollection項目數組。

但是,請注意,迭代字典值比在KeyedCollection上迭代更快,而不轉換爲數組。

請注意,迭代字典值比字典集合要快得多,快得多。

Iterate 1,000 times over collection of 10,000 items 
    Dictionary Pair: 519 ms 
Dictionary Values: 95 ms 
    Dict Val ToArray: 92 ms 
    KeyedCollection: 141 ms 
    KeyedC. ToArray: 17 ms 

計時時間來自Windows控制檯應用程序(發佈版本)。這裏是源代碼:

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace IterateCollections 
{ 
    public class GUIDkeyCollection : System.Collections.ObjectModel.KeyedCollection<Guid, GUIDkey> 
    { 
     // This parameterless constructor calls the base class constructor 
     // that specifies a dictionary threshold of 0, so that the internal 
     // dictionary is created as soon as an item is added to the 
     // collection. 
     // 
     public GUIDkeyCollection() : base() { } 

     // This is the only method that absolutely must be overridden, 
     // because without it the KeyedCollection cannot extract the 
     // keys from the items. 
     // 
     protected override Guid GetKeyForItem(GUIDkey item) 
     { 
      // In this example, the key is the part number. 
      return item.Key; 
     } 

     public GUIDkey[] ToArray() 
     { 
      return Items.ToArray(); 
     } 

     //[Obsolete("Iterate using .ToArray()", true)] 
     //public new IEnumerator GetEnumerator() 
     //{ 
     // throw new NotImplementedException("Iterate using .ToArray()"); 
     //} 
    } 
    public class GUIDkey : Object 
    { 
     private Guid key; 
     public Guid Key 
     { 
      get 
      { 
       return key; 
      } 
     } 
     public override bool Equals(Object obj) 
     { 
      //Check for null and compare run-time types. 
      if (obj == null || !(obj is GUIDkey)) return false; 
      GUIDkey item = (GUIDkey)obj; 
      return (Key == item.Key); 
     } 
     public override int GetHashCode() { return Key.GetHashCode(); } 
     public GUIDkey(Guid guid) 
     { 
      key = guid; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      const int itemCount = 10000; 
      const int repetitions = 1000; 
      const string resultFormat = "{0,18}: {1,5:D} ms"; 

      Console.WriteLine("Iterate {0:N0} times over collection of {1:N0} items", repetitions, itemCount); 

      var dict = new Dictionary<Guid, GUIDkey>(); 
      var keyd = new GUIDkeyCollection(); 

      for (int i = 0; i < itemCount; i++) 
      { 
       var d = new GUIDkey(Guid.NewGuid()); 
       dict.Add(d.Key, d); 
       keyd.Add(d); 
      } 

      var sw = new System.Diagnostics.Stopwatch(); 
      long time; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (KeyValuePair<Guid, GUIDkey> w in dict) 
       { 
        if (null == w.Value) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "Dictionary Pair", time); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (GUIDkey d in dict.Values) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "Dictionary Values", time); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (GUIDkey d in dict.Values.ToArray()) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "Dict Val ToArray", time); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (GUIDkey d in keyd) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "KeyedCollection", time); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (GUIDkey d in keyd.ToArray()) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "KeyedC. ToArray", time); 
     } 
    } 

} 
+0

第二是更快,最有可能的,因爲字典值將是稀疏。列表值取決於實施情況,可能會更好。 – Lucas 2013-04-09 14:27:12

+0

您在尋找應用程序的時間或者有特定原因不使用優化的Linq查詢時需要幾毫秒的時間? – Jasper 2013-04-09 14:28:38

+8

過早的微觀優化是邪惡的。 – JustAnotherUserYouMayKnow 2013-04-09 14:29:14

回答

8

這是比較容易檢查用秒錶:

var d = new Dictionary<string, object>(); 
var s = new List<object>(); 
for (int i =0 ; i != 10000000 ; i++) { 
    d.Add(""+i, i); 
    s.Add(i); 
} 
var sw = new Stopwatch(); 
sw.Start(); 
foreach(object o in d.Values) { 
    if (o == null) throw new ApplicationException(); 
} 
sw.Stop(); 
Console.WriteLine(sw.ElapsedMilliseconds); 
sw.Reset(); 
sw.Start(); 
foreach (object o in s) { 
    if (o == null) throw new ApplicationException(); 
} 
sw.Stop(); 
Console.WriteLine(sw.ElapsedMilliseconds); 

此打印數字,是相當接近對方:

Dict List 
---- ---- 
136 107 
139 108 
136 108 

List總是獲勝,但利潤率鑑於兩種數據結構的相對複雜性,它並不像人們預期的那麼大。

5

這是幾乎同一時間。一旦你的程序包含任何代碼,它肯定不會顯而易見。

但是,你爲什麼要聽從互聯網上的隨機人?測試它!

Stopwatch類可能會有用。

4

如果你想按鍵查找字典。
按鍵查找字典非常快,因爲這是它設計的目的。

foreach中的區別將會很小。

如果該鍵也是屬性然後再考慮KeyedCollection
KeyedCollection Class

提供了抽象基類的集合,其鍵 嵌入值。

由於Servy評論選擇具有您需要的功能的集合
.NET有許多集合。
System.Collections Namespaces

如果你的對象有一個可以表示爲Int32的自然鍵,則考慮OverRide GetHashCode()。

如果你的對象有那麼GUID的自然鍵認爲KeyedCollection和重寫GetHashCode和equals

而對於非關鍵上性能SEACH考慮LINQ,而不是用的ForEach休息;

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Collections.ObjectModel; 
using System.Diagnostics; 

namespace IntIntKeyedCollection 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 

      Guid findGUID = Guid.NewGuid(); 
      GUIDkeyCollection gUIDkeyCollection = new GUIDkeyCollection(); 
      gUIDkeyCollection.Add(new GUIDkey(findGUID)); 
      gUIDkeyCollection.Add(new GUIDkey(Guid.NewGuid())); 
      gUIDkeyCollection.Add(new GUIDkey(Guid.NewGuid())); 
      GUIDkey findGUIDkey = gUIDkeyCollection[findGUID]; // lookup by key (behaves like a dict) 
      Console.WriteLine(findGUIDkey.Key); 
      Console.WriteLine(findGUIDkey.GetHashCode()); 
      Console.WriteLine(findGUID); 
      Console.WriteLine(findGUID.GetHashCode()); 
      Console.ReadLine(); 
     } 
     public class GUIDkeyCollection : KeyedCollection<Guid, GUIDkey> 
     { 
      // This parameterless constructor calls the base class constructor 
      // that specifies a dictionary threshold of 0, so that the internal 
      // dictionary is created as soon as an item is added to the 
      // collection. 
      // 
      public GUIDkeyCollection() : base() { } 

      // This is the only method that absolutely must be overridden, 
      // because without it the KeyedCollection cannot extract the 
      // keys from the items. 
      // 
      protected override Guid GetKeyForItem(GUIDkey item) 
      { 
       // In this example, the key is the part number. 
       return item.Key; 
      } 
     } 
     public class GUIDkey : Object 
     { 
      private Guid key; 
      public Guid Key 
      { 
       get 
       { 
        return key; 
       } 
      } 
      public override bool Equals(Object obj) 
      { 
       //Check for null and compare run-time types. 
       if (obj == null || !(obj is GUIDkey)) return false; 
       GUIDkey item = (GUIDkey)obj; 
       return (Key == item.Key); 
      } 
      public override int GetHashCode() { return Key.GetHashCode(); } 
      public GUIDkey(Guid guid) 
      { 
       key = guid; 
      } 
     } 
    } 
} 
+0

爲什麼要投票? – Paparazzi 2013-04-09 14:32:20

+0

@JustAnotherUserYouMayKnow查看他在最後所做的修改。 – Paparazzi 2013-04-09 14:33:45

+0

@JustAnotherUserYouMayKnow這個答案的重點在於你應該使用適合你需要的集合類型,並且由於你從未處於一個既能滿足你的需求也不需要比較它們的速度的位置。您使用的代碼是正確的,而不是代碼更快,但不能解決您的問題。 – Servy 2013-04-09 14:36:51

2

這裏是你的測試:

class speedtest 
{ 
    static void Main(string[] args) 
    { 
     int size = 10000000; 
     Dictionary<string, object> valuesDict = new Dictionary<string, object>(); 
     List<object> valuesList = new List<object>(); 

     for (int i = 0; i < size; i++) 
     { 
      valuesDict.Add(i.ToString(), i); 
      valuesList.Add(i); 
     } 
     // valuesDict loaded with thousands of objects 

     Stopwatch s = new Stopwatch(); 
     s.Start(); 
     foreach (object value in valuesDict.Values) { /* process */ } 
     s.Stop(); 

     Stopwatch s2 = new Stopwatch(); 
     s2.Start(); 
     foreach (object value in valuesList) { /* process */ } 
     s.Stop(); 
     Console.WriteLine("Size {0}, Dictonary {1}ms, List {2}ms",size,s.ElapsedMilliseconds,s2.ElapsedMilliseconds); 

     Console.ReadLine(); 
    } 
} 

Outputs: 
Size 10000000, Dictonary 73ms, List 63ms 

但是你也應該測試,如果你已經在你的字典散列碰撞。他們可以給你不同的結果。

在真實應用程序的情況下,您必須考慮創建,訪問和記憶您的結構足跡的時間。

1

正如其他人所說,不成熟的優化yadda yadda。在正確的場景中使用正確的集合,並且只有在速度變得有問題時才擔心。

無論如何,知道的唯一方法就是衡量。我做了一個快速和骯髒的測試,它填充了一個包含30,000個對象的字典和列表,然後迭代10,000次。結果是:

詞典:2683ms
列表:1955ms

因此,它似乎是Dictionary.Values稍慢枚舉無論出於何種原因。

下面是代碼:

void Main() 
{ 
    int i = 0; 

    var dict = new Dictionary<string, object>(); 
    var list = new List<object>(); 

    for (i = 0; i < 30000; i++) 
    { 
     var foo = new Foo(); 
     dict.Add(i.ToString(), foo); 
     list.Add(foo); 
    } 

    var dictWatch = new Stopwatch(); 

    dictWatch.Start(); 

    for (i = 0; i < 10000; i++) 
    { 
     foreach (var foo in dict.Values) {} 
    } 

    dictWatch.Stop(); 
    Console.WriteLine("Dictionary: " + dictWatch.ElapsedMilliseconds); 

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

    for (i = 0; i < 10000; i++) 
    { 
     foreach (var foo in list) {} 
    } 

    listWatch.Stop(); 

    Console.WriteLine("List: " + listWatch.ElapsedMilliseconds); 
} 

class Foo {} 
相關問題