【问题标题】:How to iterate over a dictionary?如何遍历字典?
【发布时间】:2017-01-18 10:34:01
【问题描述】:

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

【问题讨论】:

  • @VikasGupta 当您不知道键将是什么时,您会建议使用键值对集合做什么?
  • @displayName 如果您想对每个键值对执行某些操作,但没有对用于查找值的键的引用,您会遍历字典,对吗?我只是指出,有时您可能会想要这样做,尽管 Vikas 声称这通常是不正确的用法。
  • 说它的用法不正确意味着有更好的选择。那是什么选择?
  • VikasGupta 是错的,我可以肯定,经过多年在非理论场景中的高性能 C# 和 C++ 编程。确实经常会创建一个字典,存储唯一的键值对,然后迭代这些值,这些值被证明在集合中具有唯一的键。创建任何进一步的集合是避免字典迭代的一种非常低效且昂贵的方法。请提供一个很好的替代方案,作为澄清您观点的问题的答案,否则您的评论非常荒谬。
  • VikasGupta 100% 正确。如果您有“一组键值对”并且不知道如何处理它,您可以字面上将其放入ICollection<KeyValuePair>(最简单的实现:List)。如果您关心“高性能编程”,那么您应该知道字典唯一更快的是通过键查找项目 - 添加items 速度较慢,iterating over a dictionary can easily take twice as long as iterating over a list.

标签: c# dictionary loops


【解决方案1】:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

【讨论】:

    【解决方案2】:

    如果您尝试在 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 及更高版本功能,您也可以在此处使用您的键/值的确切类型)

    【讨论】:

    • 我很欣赏这个答案指出您可以明确地迭代键或值。
    • 在我看来,var 应该谨慎使用。特别是在这里,它没有建设性:KeyValuePair 类型可能与问题相关。
    • var 有一个独特的目的,我不相信它是“句法”糖。有目的地使用它是一种合适的方法。
    • 在 foreach 语句中使用 var 很危险,因为有时编译器会将“var”替换为“object”,而不是使用正确的类型。
    • @JamesWierzba 我会说影响可读性的是缺少一个好的变量名(因为它是一个演示示例)。在实际代码中,如果它是 foreach (var vehicle in line.Values) { start(vehicle); },它将是可读的。
    【解决方案3】:

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

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

    【讨论】:

    • 要使用'.ElementAt'方法,记住:使用System.Linq;这不包括在 fx 中。自动生成的测试类。
    • 如果您要修改与键关联的值,这就是要走的路。否则在修改和使用foreach()时会抛出异常。
    • ElementAt 不是 O(n) 运算吗?
    • 这个答案完全不值得这么多赞成。字典没有隐式顺序,因此在这种情况下使用 .ElementAt 可能会导致细微的错误。更严重的是上面阿图罗的观点。您将迭代字典 dictionary.Count + 1 次,导致 O(n^2) 复杂度的操作应该仅为 O(n)。如果你真的需要一个索引(如果你需要,你可能首先使用了错误的集合类型),你应该迭代 dictionary.Select( (kvp, idx) =&gt; new {Index = idx, kvp.Key, kvp.Value}) 而不是在循环中使用 .ElementAt
    • ElementAt - o(n) 操作!严重地?这是你不应该怎么做的例子。这么多赞?
    【解决方案4】:

    取决于你是追求键还是值...

    来自 MSDN Dictionary(TKey, TValue) 类说明:

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

    【讨论】:

      【解决方案5】:

      一般来说,在没有特定上下文的情况下询问“最佳方式”就像在询问 什么颜色最好

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

      另一方面,在 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)
      }
      

      您可以从这些示例中执行更多实际用例。 如果不需要特定的顺序,就坚持“最直接的方式”(见上)!

      【讨论】:

      • 最后一个应该是.Values 而不是select子句。
      • @Mafii 你确定吗? OrderBy 返回的值不是 KeyValuePair 类型,它们没有 Value 字段。我在这里看到的确切类型是IOrderedEnumerable&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;。也许你的意思是别的?你能写一个完整的行来说明你的意思(并测试它)吗?
      • 我认为这个答案包含我的意思:stackoverflow.com/a/141105/5962841 但如果我混淆了,请纠正我
      • @Mafii 重新阅读我的整个答案,代码部分之间的解释说明了上下文。您提到的答案就像我的答案中的第二个代码部分(不需要顺序)。我刚刚像你建议的那样写了items.Value。在您评论的第四部分的情况下,Select() 是一种导致foreach 直接枚举字典中的值而不是键值对的方法。如果在这种情况下您不喜欢Select(),您可能更喜欢第三个代码部分。第四部分的重点是表明可以使用 LINQ 对集合进行预处理。
      • 如果你这样做.Keys.Orderby(),你将迭代一个键列表。如果这就是你所需要的,那很好。如果您需要值,那么在循环中,您必须在每个键上查询字典以获取值。在许多情况下,它不会产生实际的影响。在高性能场景中,它会。就像我在答案开头写的那样:“有很多方法(...),没有最好的方法。这取决于需要,而且通常也取决于品味。”
      【解决方案6】:

      我会说 foreach 是标准方式,尽管它显然取决于您要查找的内容

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

      这就是你要找的吗?

      【讨论】:

        【解决方案7】:

        C# 7.0 introduced Deconstructors 并且如果您使用的是 .NET Core 2.0+ 应用程序,则结构 KeyValuePair&lt;&gt; 已经为您包含了一个 Deconstruct()。所以你可以这样做:

        var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
        foreach (var (key, value) in dic) {
            Console.WriteLine($"Item [{key}] = {value}");
        }
        //Or
        foreach (var (_, value) in dic) {
            Console.WriteLine($"Item [NO_ID] = {value}");
        }
        //Or
        foreach ((int key, string value) in dic) {
            Console.WriteLine($"Item [{key}] = {value}");
        }
        

        【讨论】:

        • 如果您使用 .NET Framework,至少 4.7.2 没有 KeyValuePair 上的解构,试试这个:foreach (var (key, value) in dic.Select(x =&gt; (x.Key, x.Value)))
        【解决方案8】:

        您也可以在大型字典上尝试此方法以进行多线程处理。

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

        【讨论】:

        • @WiiMaxx 更重要的是如果这些项目不相互依赖
        【解决方案9】:

        感谢这个问题已经得到了很多回应,但我想进行一些研究。

        与迭代数组之类的东西相比,迭代字典可能会相当慢。在我的测试中,对数组的迭代耗时 0.015003 秒,而对字典(具有相同数量的元素)的迭代耗时 0.0365073 秒,是 2.4 倍!虽然我看到了更大的差异。作为比较,列表介于 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 方法做得更糟。

        【讨论】:

        • 您应该使用 StopWatch 而不是 DateTime 之类的东西进行测量:hanselman.com/blog/…
        • 能否请您描述一下您的测试场景,您的字典中有多少项目,您运行场景以计算平均时间的频率,...
        • 有趣的是,根据字典中的数据,您会得到不同的结果。在迭代字典时,枚举器函数必须跳过字典中的许多空槽,这导致它比迭代数组慢。如果字典已满,则要跳过的空槽将比半空时少。
        【解决方案10】:

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

        Dictionary<string, object> myDictionary = new Dictionary<string, object>();
        // Populate your dictionary here
        
        foreach (KeyValuePair<string,object> kvp in myDictionary)
        {
             // Do some interesting things
        }
        

        您还可以使用键和值集合

        【讨论】:

        • 支持这个.. 并感谢不使用“var”。我讨厌其中带有“var”的代码示例。除非它是“var emp = new Employee()”...人们不知道/很少知道 var 是什么。谢谢。
        • @granadaCoder 如果没有人知道var 是什么,那么你的命名就没有正确
        【解决方案11】:

        正如已在此 answer 中指出的那样,KeyValuePair&lt;TKey, TValue&gt; 从 .NET Core 2.0、.NET Standard 2.1 和 .NET Framework 5.0(预览版)开始实现 Deconstruct 方法。

        有了这个,就可以以KeyValuePair 不可知的方式遍历字典:

        var dictionary = new Dictionary<int, string>();
        
        // ...
        
        foreach (var (key, value) in dictionary)
        {
            // ...
        }
        

        【讨论】:

          【解决方案12】:

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

          【讨论】:

          • 这是不正确的。 .NET 4.7 仅内置了 ValueTuple。它可作为早期版本的 nuget 包使用。更重要的是,Deconstruct 方法需要 C# 7.0+ 作为var (fruit, number) in fruits 的解构器。
          【解决方案13】:

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

          示例:

          KeyValuePair&lt;TKey, TVal&gt; 上创建一个解构它的扩展方法:

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

          以下列方式遍历任何Dictionary&lt;TKey, TVal&gt;

          // 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}");
          }
          

          【讨论】:

          • 即使没有 Deconstruct 扩展方法,for 循环也能正常工作
          【解决方案14】:

          foreach 最快,如果只迭代___.Values,它也更快

          【讨论】:

          • 为什么在for 版本中调用ContainsKey()?这会增加您要比较的代码中不存在的额外开销。 TryGetValue() 存在以替换确切的“如果密钥存在,则使用密钥获取项目”模式。此外,如果dict 包含从0dictCount - 1 的连续整数范围,则您知道索引器不会失败;否则,dict.Keys 是您应该迭代的内容。无论哪种方式,都不需要ContainsKey()/TryGetValue()。最后,请不要发布代码截图。
          【解决方案15】:

          您建议在下面进行迭代

          Dictionary<string,object> myDictionary = new Dictionary<string,object>();
          //Populate your dictionary here
          
          foreach (KeyValuePair<string,object> kvp in myDictionary) {
              //Do some interesting things;
          }
          

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

          【讨论】:

          • 请详细说明:如果 which 值的类型为 objectforeach 将不起作用?否则这没有多大意义。
          【解决方案16】:

          迭代字典的最简单形式:

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

          【讨论】:

            【解决方案17】:

            使用 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&lt;TKey, TValue&gt;KeyValuePair 转换为强类型tuple,允许您使用这种新的舒适语法。

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

            与直接使用KeyValuePair 相比,调用用于创建tuple 的扩展方法的成本很小,如果您分配KeyValuePair 的属性Key 和无论如何Value 到新的循环变量。

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

            看看这个:MSDN Blog - New features in C# 7

            【讨论】:

            • 更喜欢“舒适”元组而不是键值对的原因是什么?我在这里看不到收益。您的元组包含一个键和一个值,键值对也是如此。
            • 您好 Maarten,感谢您的提问。主要好处是代码可读性,无需额外的编程工作。对于 KeyValuePair,必须始终使用 kvp.Keykvp.Value 的形式分别使用键和值。使用元组,您可以根据需要灵活地命名键和值,而无需在 foreach 块内使用进一步的变量声明。例如。您可以将您的键命名为factoryName,将值命名为models,这在您获得嵌套循环(字典的字典)时特别有用:代码维护变得更加容易。试一试吧! ;-)
            【解决方案18】:

            我知道这是一个非常古老的问题,但我创建了一些可能有用的扩展方法:

                public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a)
                {
                    foreach (KeyValuePair<T, U> p in d) { a(p); }
                }
            
                public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a)
                {
                    foreach (T t in k) { a(t); }
                }
            
                public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a)
                {
                    foreach (U u in v) { a(u); }
                }
            

            这样我可以写出这样的代码:

            myDictionary.ForEach(pair => Console.Write($"key: {pair.Key}, value: {pair.Value}"));
            myDictionary.Keys.ForEach(key => Console.Write(key););
            myDictionary.Values.ForEach(value => Console.Write(value););
            

            【讨论】:

              【解决方案19】:

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

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

              这是我唯一能够在继承自 DictionaryBase 的类中正常运行的类。

              【讨论】:

              • 这看起来像使用非泛型版本的字典时......即在 .NET 框架 2.0 之前。
              • @joed0tnot : 它是用于Hashtable 对象的非泛型版本
              【解决方案20】:

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

              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

              【讨论】:

              • +1 用于提高性能。确实,如果您只需要值,迭代字典本身确实会产生一些开销。这主要是由于将值或引用复制到 KeyValuePair 结构中。
              • 仅供参考,该链接中的最后一个测试是测试错误的东西:它测量迭代键,忽略值,而前两个测试确实使用该值。
              【解决方案21】:

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

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

              【讨论】:

                【解决方案22】:

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

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

                【讨论】:

                  【解决方案23】:

                  我写了一个扩展来遍历字典。

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

                  【讨论】:

                  • 您定义了一个 ForEach 方法,其中您有 foreach (...) { }... 似乎没有必要。
                  【解决方案24】:

                  如果你想使用 for 循环,你可以这样做:

                  var keyList=new List<string>(dictionary.Keys);
                  for (int i = 0; i < keyList.Count; i++)
                  {
                     var key= keyList[i];
                     var value = dictionary[key];
                   }
                  

                  【讨论】:

                  • 这有什么好处呢?它比foreach 循环更长的代码并且 性能更差,因为new List&lt;string&gt;(dictionary.Keys) 将在您有机会自己迭代之前迭代dictionary.Count 次。撇开要求“最佳方式”是主观的不谈,我不认为这将如何成为问题所寻求的“最佳方式”或“标准方式”。对于“如果你想使用 for 循环......”我会反击“不要使用for循环。”
                  • 如果你有大量的集合并且在 foreach 中操作很慢,如果你的集合在你迭代时可以改变,它可以保护你免受“集合已更改”错误的影响,并且这个解决方案比使用 ElementAt 具有更好的性能。
                  • 我同意避免“集合已修改”异常是这样做的原因之一,尽管问题中没有说明这种特殊情况,并且总是可以做到foreach (var pair in dictionary.ToArray()) { }。不过,我认为最好在答案中明确说明希望使用此代码的具体场景以及这样做的影响。
                  • 是的,你是对的,但这里有一个 ElementAt 的答案,它有很高的声誉,我输入了这个答案:)
                  【解决方案25】:

                  如果说,你想默认迭代values集合,我相信你可以实现IEnumerable,其中T是字典中values对象的类型,“this”是字典。

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

                  【讨论】:

                    【解决方案26】:
                    var dictionary = new Dictionary<string, int>
                    {
                        { "Key", 12 }
                    };
                    
                    var aggregateObjectCollection = dictionary.Select(
                        entry => new AggregateObject(entry.Key, entry.Value));
                    

                    【讨论】:

                    • 这个答案需要更多的理由/描述。 AggregateObject 添加到 KeyValuePair 是什么?问题中要求的“迭代”在哪里?
                    • Select 遍历字典并允许我们处理每个对象。它不像foreach那么通用,但我用过很多次。我的回答真的值得反对吗?
                    • 不,Select 使用迭代来影响结果,但它本身不是迭代器。迭代 (foreach) 用于的事物类型——尤其是具有副作用的操作——不在 Linq 的范围内,包括 Select。在实际枚举aggregateObjectCollection 之前,lambda 不会运行。如果将此答案视为“第一条路径”(即,在直 foreach 之前使用),则会鼓励不良做法。在某种情况下,迭代字典之前可能有一些有用的 Linq 操作,但这并不能解决所提出的问题。
                    【解决方案27】:

                    Dictionary 是c#中的一个通用集合类,以key-value格式存储数据。key必须唯一,不能为null,value可以重复和 null。因为字典中的每个项目都被视为 KeyValuePair 结构,表示一个键及其值。因此我们应该在元素的迭代过程中取元素类型KeyValuePair示例如下。

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

                    【讨论】:

                      【解决方案28】:

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

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

                      虽然这增加了一个额外的 '.ToList()' 调用,可能会有轻微的性能改进(如这里指出的 foreach vs someList.Foreach(){}), 尤其是在使用大型字典并并行运行时,这是没有选择的/根本没有效果。

                      另外,请注意,您将无法在 foreach 循环中为“Value”属性分配值。另一方面,您也可以操纵“密钥”,可能会在运行时给您带来麻烦。

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

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

                      【讨论】:

                      • 无缘无故复制整个集合将不会提高性能。它会显着减慢代码的速度,并将几乎不消耗额外内存的代码的内存占用增加一倍。
                      • 我避免使用副作用 'List.ForEach' 方法:foreach 强制提高副作用的可见性。
                      • 不幸的是,您链接到的是一个问题,而不是一个具体的答案。这个问题有 14 个答案,其中一些很长。您能否链接到支持您声明.ForEach() 可以提高性能超过foreach 的确切答案?更好的是,还要引用或描述答案的相关部分,因为即使是 Stack Overflow 链接也可能会损坏(即已删除的答案)。
                      【解决方案29】:

                      最好的答案当然是:想一想,如果你打算迭代它,你是否可以使用比字典更合适的数据结构——正如 Vikas Gupta 在(开始) 问题下的讨论。但是,作为整个线程的讨论仍然缺乏令人惊讶的好选择。一种是:

                      SortedList<string, string> x = new SortedList<string, string>();
                      
                      x.Add("key1", "value1");
                      x.Add("key2", "value2");
                      x["key3"] = "value3";
                      foreach( KeyValuePair<string, string> kvPair in x )
                                  Console.WriteLine($"{kvPair.Key}, {kvPair.Value}");
                      

                      为什么它可以说是迭代字典的代码味道(例如通过 foreach(KeyValuePair) ?

                      清洁编码的基本原则: “表达意图!” Robert C. Martin 在“干净的代码”中写道:“选择能够揭示意图的名称”。显然,仅命名太弱了。 “在每个编码决策中表达(揭示)意图”更好地表达它。

                      一个相关的原则是“Principle of least surprise”(=Principle of Least Astonishment)。

                      为什么这与迭代字典有关? 选择字典表达了选择主要用于按键查找数据的数据结构的意图。如今,.NET 中有很多替代品,如果您想遍历键/值对,您可以选择其他东西。

                      此外:如果您对某些内容进行迭代,则必须揭示有关这些项目(将要)如何订购和预期如何订购的信息! 尽管 Dictionary 的已知实现按添加的项目的顺序对键集合进行排序 - AFAIK,Dictionary 没有关于订购的可靠规范(有吗?)。

                      但是有什么选择呢?

                      TLDR:
                      SortedList:如果您的集合不是太大,一个简单的解决方案是使用 SortedList,它还可以为您提供键/值对的完整索引。

                      微软有一篇长文提到和解释合身合集:
                      Keyed collection

                      提一下最重要的:KeyedCollection 和 SortedDictionary 。 SortedDictionary 比 SortedList 快一点,仅在它变大时才插入,但缺少索引,并且仅当 O(log n) 插入优先于其他操作时才需要。如果您确实需要 O(1) 来插入并接受较慢的迭代作为交换,您必须使用简单的 Dictionary。 显然,对于所有可能的操作,没有哪种数据结构是最快的..

                      另外还有ImmutableSortedDictionary

                      如果一个数据结构不是您所需要的,那么从 Dictionary 甚至新的 ConcurrentDictionary 派生并添加显式迭代/排序函数!

                      【讨论】:

                        【解决方案30】:

                        除了排名最高的帖子还有使用之间的讨论

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

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

                        最完整的如下,因为你可以从初始化中看到字典类型,kvp是KeyValuePair

                        var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x
                        
                        foreach(var kvp in myDictionary)//iterate over dictionary
                        {
                            // do something with kvp.Value or kvp.Key
                        }
                        

                        【讨论】:

                        • 创建和复制第二个字典并不是代码可读性的有效解决方案。事实上,我认为这会使代码更难理解,因为现在你必须问自己:“为什么最后一个人要创建第二个字典?”如果您想更详细,请使用选项一。
                        • 我只是想告诉你,当 decl dict 之前为 each 时,foreach 中的用法从声明中很清楚
                        猜你喜欢
                        • 2015-11-14
                        • 1970-01-01
                        • 2018-01-01
                        • 2019-09-24
                        相关资源
                        最近更新 更多