【问题标题】:Deterministic nature of .NET Dictionary<TKey,TValue> enumeration.NET Dictionary<TKey,TValue> 枚举的确定性
【发布时间】:2020-02-06 11:14:24
【问题描述】:

我了解Dictionary&lt;TKey, TValue&gt; 中的条目顺序未定义。但是,它是确定性的吗?

也就是说,如果我以相同的顺序添加相同的元素并通过字典进行枚举,我会总是以相同的顺序获取元素,在每个运行,在每台机器上?

它似乎就是这样,但我可以依赖它吗?

【问题讨论】:

  • 不,就像在 SQL 中一样。在大多数情况下是正确的,但 ...
  • @Neil Moss:如果您在不同的工作站上计算GetHashCode,但对于相同的输入,结果可能会不同
  • 如果TKey 是string 并且randomized string hashing 已启用,则至少是不真实的,除了通常未定义(这意味着实现返回品牌在技术上是合法的每次调用都有新的顺序,即使是在未更改的字典上,尽管实际上它当然不会这样做)。
  • @NeilMoss 我已经更新了我上面的评论,在 .NET Core 中 GetHashCode() 可以在不同的运行中为相同的对象返回不同的值
  • 不仅如此,你还可以覆盖gethashcode,谁知道implementers在做什么美妙的事情

标签: c# .net dictionary deterministic


【解决方案1】:

取自此处的文档:https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?redirectedfrom=MSDN&view=netcore-3.1

出于枚举的目的,字典中的每个项目都被视为表示值及其键的 KeyValuePair 结构。返回项目的顺序是不确定的。

【讨论】:

  • 谢谢。我明白 - 虽然没有定义什么排序结果,但排序是否一致?或者,鉴于 MS 说“未定义”,我应该假设排序是not 一致的吗?
  • @NeilMoss:“未定义”借用自 C 语义(“未定义行为”),意思是“您不能假设任何事情,我们保留随时使您的假设无效的权利”。从字面上看,实现在每次调用时显式随机化结果是允许的。它所承诺的只是迭代将为您提供所有元素,但仅此而已。 (对于更实际且不那么平均的场景,假设在下一个版本中更改了实现以利用并行性,由于时间变化,顺序变得不确定。)
【解决方案2】:

谢谢大家 - 看起来答案是“否”。

String.GetHashCode 的 MSDN docs 声明:

不保证哈希码本身是稳定的。哈希码 相同的字符串可以在不同的 .NET 实现和 .NET 中有所不同 版本,以及跨 .NET 平台(例如 32 位和 64 位)的 .NET 的单一版本。在某些情况下,它们甚至可以相差 应用领域。这意味着相同的两个后续运行 程序可能会返回不同的哈希码。

如果哈希码是变体,那么元素可能最终会出现在不同的桶中,因此以不同的顺序枚举。

【讨论】:

    【解决方案3】:

    您可以混合使用列表和字典,以便获得确定性顺序

    public class DeterministicThing<TKey, TValue> :
        IEnumerable<KeyValuePair<TKey, TValue>>
        where TKey : notnull
    {
        private readonly ImmutableList<KeyValuePair<TKey, TValue>> kvpList;
        private readonly ImmutableDictionary<TKey, TValue> kvpDictionary;
    
        public DeterministicThing(IEnumerable<KeyValuePair<TKey, TValue>> kvps)
        {
            kvpList = kvps.ToImmutableList();
            kvpDictionary = kvps.ToImmutableDictionary(pair => pair.Key, pair => pair.Value);
        }
    
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => kvpList.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => kvpList.GetEnumerator();
        public TValue this[TKey key] => kvpDictionary[key];
    
        public int Count => kvpList.Count;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-14
      • 2011-04-11
      • 1970-01-01
      • 2011-01-16
      • 1970-01-01
      • 2010-11-30
      相关资源
      最近更新 更多