【问题标题】:How are hash tables implemented internally in popular languages?流行语言内部如何实现哈希表?
【发布时间】:2010-10-28 12:59:24
【问题描述】:

有人可以解释一下 Python、Ruby 等流行语言如何在内部实现哈希表以进行符号查找吗?他们是使用经典的“带有链表的数组”方法,还是使用平衡树?

我需要一种简单(更少 LOC)且快速的方法来索引用 C 编写的 DSL 中的符号。想知道其他人发现什么最有效和实用。

【问题讨论】:

  • 也许你想问“地图是如何实现的......”因为哈希表并不是实现地图的唯一方法!
  • 好评。但问题是我已经根据计算的符号哈希构建了基础工作。顺便说一句,我认为每个人都使用的除了哈希之外还有哪些地图实现方式?
  • 地图有时也是由二叉树构建的。它通常在 key 类型不可散列时使用,或者您希望保留 map 中数据的某种顺序(以便您可以从 A 迭代到 Z)。

标签: c hashtable


【解决方案1】:

平衡树有点违背哈希表的目的,因为哈希表可以在(摊销的)恒定时间内提供查找,而平衡树上的平均查找是 O(log(n))。

如果你有足够的桶,单独的链接(带有链表的数组)确实可以很好地工作,并且你的链表实现使用池分配器而不是 malloc() 单独从堆中的每个节点。我发现它在适当调整后与任何其他技术一样具有性能,并且编写起来非常容易和快速。尝试从源数据数量的 1/8 开始。

您还可以将open addressing 用于二次或多项式探测,as Python does

【讨论】:

  • 对数失败常数时间?
  • @tydok - “击败目的”意味着未能达到其他解决方案所达到的任何目标,因此它的意思是“比”差,而不是“比”。
【解决方案2】:

Attractive Chaos 有一个 comparison of Hash Table Libraries 和一个 update。 源代码是可用的,它是在 C 和 C++ 中

【讨论】:

    【解决方案3】:

    Crashworks 的意思是......

    哈希表的目的是不断地查找、添加和删除。就算法而言,所有操作的操作都是 O(1) 摊销的。 而如果您使用树...对于平衡树,最坏情况下的操作时间将是 O(log n)。 N 是节点数。但是,我们真的将哈希实现为树吗?

    【讨论】:

    • 感谢您指出我的不明确之处——我已经修正了我的答案。
    • 实现为树的哈希是前面带有类似哈希的 API 的树。
    【解决方案4】:

    您提到的经典“哈希桶数组”在我见过的每个实现中都使用过。

    最具教育意义的版本之一是 Tcl 语言中的哈希实现,在文件 tcl/generic/tclHash.c 中。文件中超过一半的行是 cmets 详细解释一切:分配、搜索、不同的哈希表类型、策略等。旁注:实现 Tcl 语言的代码是 真的 可读。

    【讨论】:

    • 早期版本的代码由于 ifdefery 的数量减少而更具可读性,尽管后来的版本在关键方面更有用(支持密钥自定义和其他类似的东西)。
    【解决方案5】:

    Perl 使用带有链表的数组来保存冲突。它有一个简单的启发式方法,可以根据需要自动将数组的大小加倍。还有一些代码可以在哈希之间共享密钥以节省一点内存。您可以在“HV”下的已过时但仍然相关的Perl Illustrated Guts 中阅读有关它的信息。如果您真的很喜欢冒险,可以深入了解hv.c

    散列算法过去很简单,但现在使用 Unicode 可能要复杂得多。因为算法是可预测的,所以存在 DoS 攻击,攻击者生成的数据会导致哈希冲突。例如,作为 POST 数据发送到网站的大量密钥列表。 Perl 程序可能会将其拆分并将其转储到一个散列中,然后将其全部推入一个桶中。结果哈希是 O(​​n) 而不是 O(1)。向服务器抛出大量 POST 请求,您可能会阻塞 CPU。结果,Perl 现在用一些随机数据扰乱了散列函数。

    您可能还想查看how Parrot implements basic hashes,它远没有 Perl 5 实现那么可怕。

    至于“最高效实用”,就用别人的hash库吧。看在上帝的份上,不要自己写一个用于生产用途。已经有无数强大而高效的了。

    【讨论】:

      【解决方案6】:

      如果您能阅读Java,您可能想查看其各种地图实现的源代码,尤其是HashMapTreeMapConcurrentSkipListMap。后两者保持键有序。

      Java 的HashMap 使用您提到的在每个存储桶位置链接的标准技术。它使用相当弱的 32 位哈希码并将键存储在表中。 Numerical Recipes 作者还给出了一个哈希表的示例(用 C 语言),其结构基本上类似于 Java,但其中 (a) 您从数组中分配桶列表的节点,并且 (b) 您使用更强大的 64 位哈希编码并省去在表中存储键。

      【讨论】:

      • 在Java中,TreeMap是基于Red-BlackTree实现的,ConcurrentSkipListMap是基于SkipList实现的.
      【解决方案7】:

      Lua 表使用utterly ingenious implemenation,对于任意键,其行为类似于“桶数组”,但如果您使用连续整数作为键,则它具有与数组相同的表示和空间开销。在实现中,每个表都有一个散列部分和一个数组部分

      我觉得这很酷:-)

      【讨论】:

        猜你喜欢
        • 2011-10-11
        • 2010-09-06
        • 2014-10-18
        • 2016-03-25
        • 2011-10-14
        • 2011-09-15
        • 1970-01-01
        • 2012-06-03
        相关资源
        最近更新 更多