字典键集奇怪

本文关键字:字典 | 更新日期: 2023-09-27 18:04:40

我有一个涉及字典的神秘情况,我从字典中枚举键,但是字典并不包含它声称包含的一些键。

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下运行的单线程 . net代码。字典是一个普通的Dictionary<uint, float>,即没有自定义相等比较器。我假设有一个哈希问题发生,因为int/int的差异,但ContainsKey(key)不应该保证为真所有键返回在字典的键集合?特别是当您只查看较低代码片段中的KeyCollection对象时,总数和包含对象的计数是关闭的,这感觉像是一个奇怪的ICollection行为。

编辑:

正如预期的那样,似乎有一个合理的解释:集合在其初始化期间被两个并发线程较早地修改了。当某物"有时断了",这是一个穿线问题,这是肯定的。显然,从多个线程访问字典可能会扰乱内部状态,使其在其生命周期的剩余时间内仅处于半功能状态,但不会引起任何异常。

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

字典键集奇怪

我没有足够的代表来评论-但我确实试图重现你的问题,但无济于事。我建议你发布GetDictionary()是如何工作的,我也建议不要像这样迭代字典,而是执行下面的操作,看看是否可以修复它:

foreach (KeyValuePair<uint, float> pair in dict)
    Console.WriteLine("[" + pair.Key + "]=" + pair.Value);

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

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

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

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

我在System中遇到了类似的奇怪行为。Uri。

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