【问题标题】:Does the Enumerator of a Dictionary<TKey, TValue> return key value pairs in the order they were added?Dictionary<TKey, TValue> 的枚举器是否按添加顺序返回键值对?
【发布时间】:2010-11-30 00:07:57
【问题描述】:

我了解字典不是有序集合,不应依赖于字典中插入和检索的顺序。

但是,这是我注意到的:

  • 向字典中添加了 20 个键值对
  • 通过执行 foreach(KeyValuePair...) 检索它们

检索顺序与添加顺序相同。 测试了大约 16 个键值对。

这是设计的吗?

【问题讨论】:

标签: c# .net collections


【解决方案1】:

来自MSDN

出于枚举的目的,字典中的每个项目都被视为一个KeyValuePair&lt;(Of &lt;(TKey, TValue&gt;)&gt;) 结构,表示一个值及其键。 物品返回的顺序未定义

[强调添加]

【讨论】:

    【解决方案2】:

    我不这么认为,字典并没有授予其中项目的内部排序。 如果您还需要保持顺序,请使用附加数据结构(数组或列表)以及字典。

    【讨论】:

      【解决方案3】:

      这是巧合,尽管可以预见。你绝对不应该依赖它。通常它发生在简单的情况下,但是如果您开始删除元素并用具有相同哈希码或只是进入相同存储桶的任何内容替换它们,该元素将占据原始元素的位置,尽管添加的时间比其他人晚。

      重现这个相对繁琐,但我不久前设法做到了for another question

      using System;
      using System.Collections.Generic;
      
      class Test
      {
          static void Main(string[] args)
          {
              var dict = new Dictionary<int, int>();        
              dict.Add(0, 0);
              dict.Add(1, 1);
              dict.Add(2, 2);
              dict.Remove(0);
              dict.Add(10, 10);
      
              foreach (var entry in dict)
              {
                  Console.WriteLine(entry.Key);
              }
          }
      }
      

      结果显示 10, 1, 2 而不是 1, 2, 10。

      请注意,如果您不执行任何删除操作,即使 当前 行为看起来总是按插入顺序生成元素,但不能保证未来的实现也会这样做......所以即使在您知道不会删除任何内容的受限情况下,也请不要依赖于此。

      【讨论】:

      • +1 - 这个例子帮助我重现了我的场景中的行为,所以我可以“看到”它在行动中,帮助我避免潜在的陷阱。
      • 我问了一个有点相关的问题here 代码的 sn-p,当在 .NET Core 3.1 中使用 String 作为键时,它表现出ImmutableDictionary 枚举的不确定性.这提供了一个示例,现在 current 行为不会按插入顺序产生元素(尽管对于 ImmutableDictionary)。
      【解决方案4】:

      我相信枚举Dictionary&lt;K,V&gt; 将按照它们插入的顺序返回键如果所有键散列到相同的值。这是因为Dictionary&lt;K,V&gt; 实现使用键对象的哈希码将键/值对插入到桶中,并且值(通常)按照插入的顺序存储在桶中。如果您在用户定义的对象中一直看到这种行为,那么您可能没有(正确地)覆盖GetHashCode() 方法?

      【讨论】:

        【解决方案5】:

        根据设计,Dictionary&lt;TKey,TValue&gt; 不是有序结构,因为它主要用于更多基于密钥的访问。

        如果您需要按特定顺序检索项目,您应该查看Sorted Dictionary&lt;TKey, TValue&gt;,它采用Comparer&lt;T&gt;,用于对Sorted Dictionary&lt;TKey, TValue&gt; 中的键进行排序。

        【讨论】:

        • 我想你可能误读了这个问题。他在问他正在按顺序查看结果这一事实是否是设计使然。
        • 感谢乔恩,我的意思是字典不是有序结构的事实是设计使然。我会更好地澄清我的答案(在火车上用 iPhone 回答可能会很棘手:))
        • @Russ:是的 - 我明白你在说什么,但它没有回答被问到的“这是设计使然”的问题 :)
        【解决方案6】:

        如果您想以固定顺序遍历字典,您可以尝试OrderedDictionary

        【讨论】:

          【解决方案7】:

          这是设计使然吗?它可能不在最初的 .Net Framework 2.0 中,但现在有一个隐含的约定,它们将按照添加的顺序进行排序,因为改变它会破坏很多依赖于原始泛型行为的代码字典。与 Go 语言相比,他们的地图故意返回随机排序,以防止地图用户依赖任何排序 [1]。

          框架编写者对 Dictionary&lt;T,V&gt; 所做的任何改进或更改都必须保留该隐式契约。

          [1] "自 Go 1.0 发布以来,运行时地图迭代顺序随机化。", https://blog.golang.org/go-maps-in-action .

          【讨论】:

          • 您能否分享一些文档或文章,我可以在其中阅读更多关于保留插入订单的默认合同的信息,好吗?我找不到任何东西。
          • 没有合同说它保持秩序。事实上,根据 Jon Skeet 的回答,当有删除时,排序就会崩溃。我只是认为当前的行为不太可能改变。
          猜你喜欢
          • 2010-10-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-24
          相关资源
          最近更新 更多