【问题标题】:Order-preserving data structures in C#C# 中的保序数据结构
【发布时间】:2010-04-27 15:55:03
【问题描述】:

MSDN 没有关于数据结构的顺序保留属性的信息。所以我一直在做这样的假设:

  • HashTable 和 HashSet 保留插入顺序(即其中的“哈希”是赠品)
  • 字典和列表保留插入顺序。

据此我推断,如果我有一个定义曲线的Dictionary<double, double> foofoo.Keys.ToList()foo.Values.ToList() 会给我一个有序列表,列出该曲线的范围和域,而不会弄乱它?

【问题讨论】:

    标签: c# data-structures


    【解决方案1】:

    您不应期望常规Dictionary<TKey,TValue> 中的键或值以任何顺序进行维护。在SortedDictionary<TKey,TValue> 中,键和值按键的值顺序维护 - 这与插入顺序不同

    .NET 框架中唯一保留插入顺序的内置字典是System.Collections.Specialized.OrderedDictionary。不幸的是,这个类不是通用的——然而,围绕它编写一个通用的包装器并不难。请记住,在处理值类型(如intdouble)时,它会导致键/值的装箱(通用字典不会对值类型进行装箱)。

    【讨论】:

    • 这是我最初的假设,但后来更多的谷歌搜索似乎表明 Dictionary 确实保留了插入顺序。 OrderedDictionary 是。谢谢!
    • 奥伦,你能粘贴一些你找到的关于字典保留插入顺序的链接吗?
    【解决方案2】:

    我已经对 Dictionary 排序进行了广泛的测试。 我发现了这个: 只要我将新的键/值对添加到集合中,就会保持顺序。 如果我替换了一个已经存在的元素,或者更糟糕的是,如果我删除了一些元素,则以下插入不保证在字典的尾部

    在一个特定的情况下,我有一个非常讨厌的错误,假设字典键的顺序与插入的顺序完全相同,我发现这个错误是键的意外重新分配,因此顺序没有保留。

    为了保持顺序,您必须包装对字典的所有写访问(除了 add)并编写如下伪算法:

    1- 将字典复制到常规列表(键/值对) 2-删除/替换/修改列表 3- 清除字典并将所有键/值对重新添加回字典

    您愿意使用 Dictionary 构造函数预先分配足够的元素来存储您的实际列表,等于 list.count+1

    这种丑陋的方法解决了这个错误,但我可以向你保证 Dictionary 不会保留键插入顺序,除非只是在尾部插入。

    【讨论】:

      【解决方案3】:

      正如@Anton 指出的那样,Dictionary<TKey,TValue> 是一个无序集合。正确返回您的价值观是巧合,最终会失败。如果你需要一个有序的哈希表,你应该使用SortedDictionary<TKey,TValue>

      【讨论】:

      • SortedDictionary<TK,TV> 不保留插入顺序,它根据键的自然顺序维护项目。在System.Collections.Specialized 命名空间中有一个非泛型OrderedDictionary确实 以额外存储为代价保留插入顺序。 (它基本上实现为一个哈希表和一个列表)。
      • 感谢您区分“插入顺序”和“排序顺序”。互联网上到处都有人将这个概念与 C# 中的排序字典混淆:-/
      【解决方案4】:

      无论如何,依靠Dictionary<TKey, TValue> 来保持排序!

      虽然Dictionary<TKey, TValue> 明确指出枚举顺序未定义,但我们测试它确实保留了插入顺序(至少只要您不从中删除项目)。如果有人可以提供反驳它的测试,我们会非常感兴趣,因为我们的生产代码依赖于它。

      您可能会采用相同的方法,为自己节省一些精力,为您的客户节省一些钱。

      当然,Microsoft 可能会在未来的 .NET 版本中更改 Dictionary 实现,但如果发生这种情况,您的自动化测试会检测到它,届时您可以将 Dictionary 替换为另一个容器,对吧?

      【讨论】:

      • 您在在生产代码中使用此功能???你疯了?字典确实 not 保留插入顺序,它在您的情况下所做的事实在很大程度上是您使用它的边缘情况和副作用。您可以通过查看反射器中的 Insert 方法来看到这一点 - 它使用密钥哈希码来确定将条目放在后备数组中的位置,然后只遍历其中的数组 Enumerator
      • @thecoop:我确实同意依赖时间排序是危险的,但还有更多的事情导致Dictionary 以这种方式行事,而不仅仅是边缘情况。它实际上维护了两个数组。一个用于存储的实际项目(条目),一个用于索引到前者(存储桶)。无论哈希码和存储桶数组发生什么情况,新项目总是添加到条目数组中的下一个可用槽中。即使您删除一个项目,该条目数组的顺序仍然是确定的(尽管不再是临时的)。同样,我永远不会依赖这个细节。
      • 相信我,我们和你一样感到惊讶,但尽管我们尽可能地尝试,我们只是无法编写破坏我们生产代码的测试(在那一点上,无论如何,呵呵)。我们还编写了一个测试,在 Dictionary 中以随机顺序插入一百万个项目,对其进行迭代并断言元素以与插入相同的顺序出现。再一次,自动测试会告诉我们将来是否由于某种原因我们不能依赖它。
      • 我认为这里的重点是,虽然它目前可能有效,但将来可能无效。你可能有很好的自动测试可以捕捉到这一点,但这只是额外的工作,如果你从一个更好的设计开始,这个设计基于一个保证保持顺序的集合(例如OrderedDictionary) .所以我认为这对于处于设计软件阶段的人来说不是一个好的建议。
      • 是的,你相信微软不会改变你的行为。如果他们不保证秩序,你的命运就掌握在他们手中。
      猜你喜欢
      • 1970-01-01
      • 2018-08-30
      • 2016-05-02
      • 2010-09-27
      • 2012-05-03
      • 2011-03-19
      • 2021-11-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多