4

我有一个涉及字典的神秘情况,我从字典中枚举键,但字典不包含它所说的某些键。

Dictionary<uint, float> dict = GetDictionary(); // Gets values, 6268 pairs
foreach(uint key in dict.Keys)
{
   if (!dict.ContainsKey(key))
      Console.WriteLine("Wat? "+key);
}

以上将打印 6268 个密钥中的两个。这两个键没有什么特别之处,都是小于 Int32.MaxValue 的正值(369099203 和 520093968)。

对计数的检查揭示了这一点:

Console.WriteLine(dict.Count);                           // 6268
Console.WriteLine(dict.Keys.Count());                    // 6268
Console.WriteLine(dict.Keys.Count(dict.Keys.Contains));  // 6266 

这是在 .NET4.5 CLR 下运行的单线程.NET4 代码。字典是香草,Dictionary<uint, float>没有自定义相等比较器。我假设由于 uint/int 差异而发生哈希问题,但不ContainsKey(key)应该保证字典的 Key 集合中返回的所有键都是真的吗?尤其是当您只查看下面代码片段中的 KeyCollection 对象时,总计数和包含对象的计数是关闭的,这感觉像是一种奇怪的ICollection行为。

编辑:

正如预期的那样,似乎有一个合理的解释:集合在初始化期间两个并发线程修改过。当某些东西“有时会中断”时,这是一个线程问题,果然如此。从多个线程访问一个 dict 显然会扰乱内部状态,使其在其生命周期的剩余时间内处于半功能状态,但不会导致任何异常。

我要切换到并发字典,并可能删除这个问题。谢谢。

4

4 回答 4

1

我没有足够的代表发表评论 - 但我确实试图重现您的问题但无济于事。我会建议你发布 GetDictionary() 是如何工作的,我也建议不要像那样遍历字典,而是在下面做,看看是否能解决它:

foreach (KeyValuePair<uint, float> pair in dict)
    Console.WriteLine("[" + pair.Key + "]=" + pair.Value);
于 2013-03-19T21:04:12.840 回答
1

GetDictionary() 在构造字典时是否有可能添加自定义键相等比较器?如果是这样,问题可能与比较器实现有关。

于 2013-03-19T21:10:04.753 回答
0

我也遇到了 System.Uri 类似的奇怪行为。

结果是存储在字典中的键和我用来查找的键之间的架构不匹配。特别是,存储在字典中的 Uri 是 32 位的,而我正在寻找 64 位的。显然,由于不同架构之间的 GetHashcode() 不相等,因此字典无法匹配键。

于 2013-03-19T22:04:29.517 回答
0

重要提示:当我在GetHashCode整个帖子中提到时,我指的是IEqualityComparer<T>.GetHashCode. 默认情况下,字典将使用EqualityComparer<T>.Default,它将返回调用GetHashCode键本身的结果。但是,您可以在创建字典时提供特定的实现IEqualityComparer<T>以使用不同的行为。

如果键的结果GetHashCode在值添加到字典的时间和您枚举键的时间点之间发生变化,则可能会发生这种情况。当您枚举键时,它会返回数组中所有填充的“桶”的键。但是,当您查找特定键时,它会根据键的结果重新计算预期的存储桶GetHashCode。如果哈希码发生变化,那么字典桶中键/值对的实际位置和预期位置可能不再相同,在这种情况下Contains将返回 false。

您应该确保GetHashCode在将值添加到键的字典后,字典中键的结果不会改变。

于 2013-03-19T21:16:55.320 回答