【问题标题】:Hashtable collision rehashing - how are values read?哈希表冲突重新散列 - 如何读取值?
【发布时间】:2012-02-28 08:05:37
【问题描述】:

我正在尝试了解 Hashtables 在 C# 中的工作原理。我阅读了 MSDN 文章,了解到 C# Hashtables 使用“重新散列”处理冲突,即如果我尝试将键/值对插入到哈希表中,如果使用 HashFunction H1 导致冲突,那么它将尝试 HashFunction H2、H3等,直到没有发现碰撞。

MSDN报价:

Hashtable 类使用一种不同的技术,称为 重演。 (一些来源将重新散列称为双重散列。)

重新散列的工作原理如下:有一组不同的散列 函数,H1 ... Hn,以及在从 哈希表,最初使用 H1 哈希函数。如果这导致 如果发生碰撞,则尝试使用 H2,并在需要时尝试直至 Hn。 上一节只展示了一个哈希函数,即 初始哈希函数(H1)。其他哈希函数非常相似 到这个函数,只用一个乘法因子来区分。在 一般来说,散列函数Hk定义为:

Hk(key) = [GetHash(key) + k * (1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1)))] % hashsize

但是,以 MSDN 站点 1 为例:

private static Hashtable employees = new Hashtable();

public static void Main()
{
    // Add some values to the Hashtable, indexed by a string key
    employees.Add("111-22-3333", "Scott");
    employees.Add("222-33-4444", "Sam");
}

假设添加第二个键会导致冲突,因此必须使用 H2。但是,当我调用 employees["222-33-4444"] 时,哈希表是如何知道使用 H2 的?有单独的映射吗?谢谢。

【问题讨论】:

  • 如果你引用一个链接,你应该包括它。

标签: c# hashtable


【解决方案1】:

我认为你误解了重新散列。只有一个哈希函数:虚拟object.GetHashCode()(或者,如果您提供 IHashCodeProvider 或 IEqualityComparer,它会使用该对象来计算哈希码)。当哈希表已满时,它会扩展其容量并将元素重新分配到新的更大的数组中。执行此操作的私有方法称为Rehash(),但它不会重新计算哈希码。

更正

rehashing不使用新的函数,而是对hash码的前一个值进行操作;这具有搜索后续槽的效果,直到找到一个空槽(用于插入/设置)或直到所有具有相同(初始)哈希码的键都已检查是否与索引键相等(用于检索)。

编辑

直接回答您的问题:

假设添加第二个键会导致冲突,因此必须使用 H2。但是,当我调用 employees["222-33-4444"] 时,哈希表是如何知道使用 H2 的?有单独的映射吗?谢谢。

  1. 根据传递的密钥的哈希码计算正确的存储桶。
  2. 如果该存储桶为空,则失败。
  3. 如果存储桶的键与传递的键匹配,则返回存储桶的值。
  4. 如果哈希冲突计数为零,则失败。
  5. 根据当前哈希码计算下一个哈希码。
  6. 根据新的哈希码计算正确的桶。
  7. 转到第 2 步。

【讨论】:

  • 事实上Hashtable 确实使用了多个哈希函数,请参阅带引号的更新问题,因此您的答案不正确。
  • @BrokenGlass 我非常怀疑除了GetHashCode() 之外的任何散列都被使用。由此产生的桶计算可以通过多种方式来解决桶索引的冲突,但几乎不可能对完整的哈希码冲突做任何事情。
  • @CodeInChaos:这就是 MSDN 链接所说的 - 请记住,这仅适用于 pre-generics Hashtable
  • @BrokenGlass 在查看反编译的HashTable 类后,我已经更正了答案。错误信息是基于我对 Dictionary<K, V>HashSet<T> 的哈希表实现更加熟悉,这有些不同。
【解决方案2】:

哈希表在哈希表本身中存储键和值。这样以后在诸如哈希表查找之类的操作期间,可以保证找到的值与用于查找的索引匹配。哈希表使用简单的“尝试查找直到成功的基本方法”方法。在这种情况下,查找的方法是“使用哈希函数 X”,其中 X 在失败时更改。

在其他方案中,查找方法是“查看表条目 X”(由散列函数确定),其中 X 只是以包装方式在每次失败时增加一。

现在令人烦恼的问题是,当表中的值 ISN'T 时会发生什么?好吧,这可能相当难看:当您点击表中丢失的条目时,或者更糟糕的是,当您遍历表中存储的尽可能多的条目时,您可以确定该条目是不在那里——但在最坏的情况下,这可能需要“一段时间”。

请记住,由于只有一个值可以与一个键关联,因此一旦找到键,您就找到了值。哈希表可以做的最糟糕的事情是不得不对哈希表本身的所有值进行相当于缓存不友好的线性搜索......但最终,如果它存在,它会找到该值,因为它将存储的键与请求的密钥以测试它是否存在。封闭哈希表的唯一优化是首先查看哪里——在这种情况下,哈希函数 1 表示,然后是 2,然后是 3...

【讨论】:

  • 当您提到“价值”时,我假设您指的是真正的“关键”(“222-33-4444”)?即您的“键”是散列键,值是“222-33-4444”,这只是散列键的抽象?
  • Hashtable 类使用计数来指示在给定的初始哈希码上发生了多少哈希冲突;这可以防止它检查包含具有不同初始哈希码值的键的非空桶。
  • @user981225:“111-22-3333”将是“关键”,而“Scott”将是我所说的值。我只是想澄清一下,不仅存储了“值”——因此它确实可以检查以确保它找到的索引是您请求的索引。
  • @phoog:很有趣!但是,我要补充一点,如果我理解正确,它有时仍会检查包含仅具有不同初始哈希码值的键的非空存储桶,但这只会在从表中删除条目时发生。
【解决方案3】:

它将首先尝试 H1。如果没有找到匹配项,它将使用 H2。以此类推。

【讨论】:

    猜你喜欢
    • 2017-06-01
    • 2011-09-04
    • 1970-01-01
    • 2012-12-25
    • 2017-05-03
    • 1970-01-01
    • 2010-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多