哪个.NET集合更快:枚举foreach Dictionary 。值或List ?

这些枚举中的一个是否比另一个或大约相同? (C#中的例子)

情况1:

Dictionary valuesDict; // valuesDict loaded with thousands of objects foreach (object value in valuesDict.Values) { /* process */ } 

案例2:

 List valuesList; // valuesList loaded with thousands of objects foreach (object value in valuesList) { /* process */ } 

更新:

背景:

字典对于其他地方的键控搜索是有益的(而不是通过列表迭代),但是如果遍历字典比通过列表慢得多,那么好处将会减少。

更新:接受许多人的建议,我已经完成了自己的测试。

首先,这些是结果。 以下是该计划。

迭代整个收集词典:78 Keyd:131列表:76

键控搜索集合Dict: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 { protected override string GetKeyForItem(Data item) { return item.Id; } } class Program { static void Main(string[] args) { var dict = new System.Collections.Generic.Dictionary(); var list = new System.Collections.Generic.List(); 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  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 { // 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(); 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 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); } } } 

使用秒表检查相对容易:

 var d = new Dictionary(); var s = new List(); 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总是获胜,但考虑到两个数据结构的相对复杂性,边距并不像人们预期的那么大。

大约在同一时间。 当您的流程包含任何代码时,它肯定不会是显而易见的。

但是你为什么要听互联网上随机的人呢? 测试一下!

秒表类可能很有用。

如果你想按键查找,那么字典。
按键进行字典查找非常快,因为这是它的设计目的。

foreach的差异很小。

如果密钥也是属性,那么考虑KeyedCollection
KeyedCollection类

为其键嵌入值的集合提供抽象基类。

正如Servy所评论的那样,选择具有您所需function的系列
.NET有很多集合。
System.Collections命名空间

如果你的对象有一个可以表示为Int32的自然键,那么考虑OverRide GetHashCode()。

如果您的对象具有GUID的自然键,则考虑KeyedCollection和OverRide GetHashCode和Equals

对于nonKey属性的搜索,考虑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 { // 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; } } } } 

这是你的测试:

 class speedtest { static void Main(string[] args) { int size = 10000000; Dictionary valuesDict = new Dictionary(); List valuesList = new List(); 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 

但是,您还应该测试字典中是否存在哈希冲突。 他们可以给你一个不同的结果。

在真实应用程序的情况下,您必须考虑创建,访问和记忆结构的脚印花费的时间。

正如其他人所说,yadda yadda过早优化。 对正确的场景使用正确的集合,只有在成为问题时才会担心速度。

无论如何,唯一知道的方法就是衡量。 我做了一个快速而肮脏的测试,它填充了一个字典和一个包含30,000个对象的列表,然后迭代它们10,000次。 结果是:

字典:2683ms
清单:1955ms

因此,无论出于何种原因,似乎Dictionary.Values的枚举速度稍慢。

这是代码:

 void Main() { int i = 0; var dict = new Dictionary(); var list = new List(); 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 {}