提问



我已经看到了几种不同的方法来迭代C#中的字典。有标准方法吗?

最佳参考


foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

其它参考1


如果您尝试在C#中使用通用字典,则可以使用另一种语言的关联数组:


foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}


或者,如果您只需要遍历密钥集合,请使用


foreach(var item in myDictionary.Keys)
{
  foo(item);
}


最后,如果你只对这些价值感兴趣:


foreach(var item in myDictionary.Values)
{
  foo(item);
}


(注意var关键字是一个可选的C#3.0及以上功能,你也可以在这里使用你的键/值的确切类型)

其它参考2


在某些情况下,您可能需要一个可以通过for循环实现提供的计数器。为此,LINQ提供了ElementAt,它可以实现以下功能:[63]


for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}

其它参考3


取决于你是否关键或价值观......


来自MSDN Dictionary(TKey, TValue)类描述:[64]


// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach( KeyValuePair<string, string> kvp in openWith )
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

// To get the values alone, use the Values property.
Dictionary<string, string>.ValueCollection valueColl =
    openWith.Values;

// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach( string s in valueColl )
{
    Console.WriteLine("Value = {0}", s);
}

// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl =
    openWith.Keys;

// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
    Console.WriteLine("Key = {0}", s);
}

其它参考4


一般来说,在没有特定背景的情况下询问最好的方式就像问什么是最好的颜色。


一方面,有很多颜色,没有最好的颜色。这取决于需要,往往也取决于口味。


另一方面,有许多方法可以在C#中迭代一个字典,并且没有最好的方法。这取决于需要,通常也取决于品味。


最直接的方式



foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}


如果只需要值(允许调用它item,比kvp.Value更可读)。


foreach (var item in items.Values)
{
    doStuff(item)
}


如果您需要特定的排序顺序



通常,初学者对词典枚举的顺序感到惊讶。


LINQ提供了一种简洁的语法,允许指定顺序(和许多其他东西),例如:


foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}


您可能只需要该值。 LINQ还提供简洁的解决方案:



  • 直接迭代该值(允许调用它item,比kvp.Value更可读)

  • 但按键排序



这里是:


foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
    doStuff(item)
}


您可以从这些示例中获得更多真实用例。
如果您不需要特定订单,只需坚持最直接的方式(见上文)!

其它参考5


我会说foreach是标准的方式,虽然它显然取决于你在寻找什么


foreach(var kvp in my_dictionary) {
  ...
}


那是你在寻找什么?

其它参考6


您也可以在大字典上尝试使用多线程处理。


dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});

其它参考7


有很多选择。我最喜欢的是KeyValuePair


Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}


您还可以使用键和值集合

其它参考8


我很欣赏这个问题已经有了很多回复,但我想进行一些研究。


与迭代类似数组的东西相比,迭代字典可能相当慢。在我的测试中,对数组的迭代花费了0.015003秒,而对字典的迭代(具有相同数量的元素)花费了0.0365073秒,这是2.4倍的长度!虽然我看到了更大的差异。为了比较,List在某处介于0.00215043秒之间。


然而,这就像比较苹果和橘子。我的观点是迭代字典很慢。


字典针对查找进行了优化,因此考虑到这一点,我已经创建了两个方法。一个简单地执行foreach,另一个迭代密钥然后查找。


public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return "Normal";
}


这个加载密钥并迭代它们(我也尝试将密钥拉成字符串[[]]但差别可以忽略不计。


public static string Keys(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var key in dictionary.Keys)
    {
        value = dictionary[key];
        count++;
    }

    return "Keys";
}


在这个例子中,正常的foreach测试花了0.0310062,密钥版本花了0.2205441。加载所有键并迭代所有查找显然要慢得多!


为了进行最后的测试,我已经执行了十次迭代,看看在这里使用密钥是否有任何好处(此时我只是好奇):


这是RunTest方法,如果这可以帮助您可视化正在发生的事情。


private static string RunTest<T>(T dictionary, Func<T, string> function)
{            
    DateTime start = DateTime.Now;
    string name = null;
    for (int i = 0; i < 10; i++)
    {
        name = function(dictionary);
    }
    DateTime end = DateTime.Now;
    var duration = end.Subtract(start);
    return string.Format("{0} took {1} seconds", name, duration.TotalSeconds);
}


正常的foreach运行时间为0.2820564秒(大约是单次迭代的十倍 - 正如您所期望的那样)。对键的迭代花费了2.2249449秒。


已编辑添加:
阅读其他一些答案让我怀疑如果我使用Dictionary而不是Dictionary,会发生什么。在此示例中,数组占用0.0120024秒,列表0.0185037秒,字典0.0465093秒。期望数据类型对字典的缓慢程度产生影响是合理的。


我的结论是什么?



  • 如果可以的话,避免迭代字典,它们比迭代使用相同数据的数组慢得多。

  • 如果你确实选择迭代字典,不要试图太聪明,虽然速度慢,但你可能比使用标准的foreach方法做得更糟。


其它参考9


您建议在下面进行迭代


Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary) {
    //Do some interesting things;
}


仅供参考,foreach如果值是对象类型则不起作用。

其它参考10


使用.NET Framework 4.7,可以使用分解


var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
    Console.WriteLine(fruit + ": " + number);
}


要使此代码适用于较低的C#版本,请添加System.ValueTuple NuGet package并在某处写入


public static class MyExtensions
{
    public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple,
        out T1 key, out T2 value)
    {
        key = tuple.Key;
        value = tuple.Value;
    }
}

其它参考11


迭代字典的最简单形式:


foreach(var item in myDictionary)
{ 
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}

其它参考12


有时,如果您只需要枚举值,请使用字典的值集合:


foreach(var value in dictionary.Values)
{
    // do something with entry.Value only
}


这篇文章报道说它是最快的方法:
http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html[65]

其它参考13


我在MSDN上的DictionaryBase类的文档中找到了这个方法:


foreach (DictionaryEntry de in myDictionary)
{
     //Do some stuff with de.Value or de.Key
}


这是我能够在从DictionaryBase继承的类中正确运行的唯一一个。

其它参考14


我将利用.NET 4.0+并提供最初接受的答案的更新答案:


foreach(var entry in MyDic)
{
    // do something with entry.Value or entry.Key
}

其它参考15


根据MSDN上的官方文档,迭代字典的标准方法是:


foreach (DictionaryEntry entry in myDictionary)
{
     //Read entry.Key and entry.Value here
}

其它参考16


使用 C#7 ,将此扩展方法添加到解决方案的任何项目中:


public static class IDictionaryExtensions
{
    public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>(
        this IDictionary<TKey, TValue> dict)
    {
        foreach (KeyValuePair<TKey, TValue> kvp in dict)
            yield return (kvp.Key, kvp.Value);
    }
}


点击
并使用这个简单的语法


foreach (var(id, value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


点击
或者这个,如果你愿意的话


foreach ((string id, object value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


点击
代替传统


foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

    // your code using 'id' and 'value'
}


点击
扩展方法将IDictionary<TKey, TValue>KeyValuePair转换为强类型tuple,允许您使用这种新的舒适语法。


它将-just-所需的字典条目转换为tuples,因此它不会将整个字典转换为tuples,因此没有与此相关的性能问题。


与直接使用KeyValuePair相比,只需要很少的费用来调用创建tuple的扩展方法,如果分配KeyValuePair的属性,则不应该成为问题[[无论如何,KeyValue到新的循环变量。


在实践中,这种新语法非常适合大多数情况,除了低级超高性能场景,您仍然可以选择不在特定位置使用它。


看看这个:MSDN博客 - C#7中的新功能[66]

其它参考17


如果说,你想默认迭代值集合,我相信你可以实现IEnumerable<>,其中T是字典中值对象的类型,this是一个Dictionary。


public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}

其它参考18


从C#7开始,您可以将对象解构为变量。我相信这是迭代字典的最佳方式。


示例:


KeyValuePair<TKey, TVal>上创建一个解构它的扩展方法:


public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey, out TVal val)
{
   key = pair.Key;
   val = pair.Value;
}


以下列方式迭代任何Dictionary<TKey, TVal>


// Dictionary can be of any types, just using 'int' and 'string' as examples.
Dictionary<int, string> dict = new Dictionary<int, string>();

// Deconstructor gets called here.
foreach (var (key, value) in dict)
{
   Console.WriteLine($"{key} : {value}");
}

其它参考19


var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));

其它参考20


只是想加上我的2美分,因为大多数答案与foreach-loop有关。
请看下面的代码:


Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>();

//Add some entries to the dictionary

myProductPrices.ToList().ForEach(kvP => 
{
    kvP.Value *= 1.15;
    Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value));
});


Altought这增加了一个.ToList()的附加调用,可能会有轻微的性能提升(正如这里指出的foreach与someList.Foreach(){}),
特别是在处理大型字典并且并行运行时,没有选择/根本没有效果。


此外,请注意,您无法为foreach循环中的Value属性赋值。另一方面,您也可以操纵密钥,可能会在运行时遇到麻烦。


当您只想读取键和值时,您也可以使用IEnumerable.Select()。


var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );

其它参考21


我写了一个扩展来循环字典。


public static class DictionaryExtension
{
    public static void ForEach<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> action) {
        foreach(KeyValuePair<T1, T2> keyValue in dictionary) {
            action(keyValue.Key, keyValue.Value);
        }
    }
}


然后你可以打电话


myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));

其它参考22


字典< TKey,TValue> 它是c#中的泛型集合类,它以键值格式存储数据.Key必须是唯一的,它不能为null,而值可以是重复的和null。作为每个项目在字典被视为KeyValuePair< TKey,TValue>表示键及其值的结构。因此我们应该采用元素类型KeyValuePair< TKey,TValue>在元素的迭代过程中。以下是示例。


Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"One");
dict.Add(2,"Two");
dict.Add(3,"Three");

foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}

其它参考23


字典是特殊列表,而列表中的每个值都有一个键
   这也是一个变量。字典的一个很好的例子是电话簿。


   Dictionary<string, long> phonebook = new Dictionary<string, long>();
    phonebook.Add("Alex", 4154346543);
    phonebook["Jessica"] = 4159484588;


请注意,在定义字典时,我们需要提供泛型
   定义有两种类型 - 键的类型和值的类型。在这种情况下,键是一个字符串,而值是一个整数。


还有两种方法可以使用括号运算符或使用Add方法向字典添加单个值。


要检查字典中是否有某个键,我们可以使用ContainsKey方法:


Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

if (phonebook.ContainsKey("Alex"))
{
    Console.WriteLine("Alex's number is " + phonebook["Alex"]);
}


要从字典中删除项目,我们可以使用Remove方法。通过键从字典中删除项目非常快速且非常高效。使用其值从List中删除项目时,该过程缓慢且效率低,与字典删除功能不同。


Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

phonebook.Remove("Jessica");
Console.WriteLine(phonebook.Count);