Python dict如何在碰撞发生时存储关键值?

时间:2014-02-06 05:49:57

标签: python dictionary hashtable

Python如何存储dict键,在哈希表中发生冲突时的值?什么是用于获取哈希值的哈希算法?

2 个答案:

答案 0 :(得分:11)

对于“普通”Python,Praveen Gollakota的this great writeup解释得非常好,这里有重要的内容:

  • Python字典作为哈希表实现。散列表由插槽组成,密钥通过散列函数映射到插槽。
  • 散列表实现必须允许散列冲突,即使两个键具有相同的散列值,表的实现必须具有明确插入和检索键和值对的策略。
  • Python dict使用开放式寻址来解决哈希冲突(参见dictobject.c:296-297)。
  • 在开放寻址中,通过探测解决哈希冲突(在下面解释)。
  • 哈希表只是一个连续的内存块(就像一个数组,所以你可以通过索引进行O(1)查找)。
  • 哈希表中的每个插槽可以存储一个且只能存储一个条目。这很重要。
  • 表格中的每个条目实际上是三个项目的组合 - <hash, key, value>。这是作为C结构实现的(参见dictobject.h:51-56)。
  • 初始化新的dict时,它以8个插槽开始。 (见dictobject.h:49)
  • 在向表中添加条目时,我们从一些基于密钥哈希的插槽i开始。 CPython使用初始i = hash(key) & mask,其中mask = PyDictMINSIZE - 1,但这并不重要。请注意,检查的初始插槽i取决于密钥的哈希值。
  • 如果该插槽为空,则该条目将添加到插槽中(通过输入,我的意思是<hash|key|value>)。但如果那个插槽被占用怎么办?很可能是因为另一个条目具有相同的哈希值(哈希冲突!)
  • 如果插槽被占用,CPython(甚至PyPy)会比较插槽中的条目与密钥的散列和密钥(通过比较我的意思= =比较而不是比较)要插入的当前条目(dictobject.c 337,344-345)。如果两者都匹配,则认为该条目已存在,放弃并继续下一个要插入的条目。如果散列或密钥不匹配,则开始探测。
  • 探测只是意味着它按插槽搜索插槽以找到空插槽。从技术上讲,我们可以逐个进行i+1i+2,...并使用第一个可用的(线性探测)。但由于评论中详细解释的原因(见dictobject.c:33-126),CPython使用随机探测。在随机探测中,以伪随机顺序拾取下一个时隙。该条目将添加到第一个空槽中。对于此讨论,用于选择下一个时隙的实际算法并不重要(有关探测算法,请参阅dictobject.c:33-126)。重要的是探测插槽直到找到第一个空插槽。
  • 查找同样的事情,只是从初始插槽i开始(其中i取决于密钥的散列)。如果散列和密钥都与插槽中的条目不匹配,则它开始探测,直到找到具有匹配的插槽。如果所有插槽都耗尽,则报告失败。
  • 为了避免减慢查找速度,dict将在三分之二满时重新调整大小(请参阅dictobject.h:64-65)。

答案 1 :(得分:0)

简短版本:Python规范没有指定字典实现,但CPython使用哈希映射并处理与开放寻址的冲突。

请参阅this answer to a similar question以及Wikipedia page on hash tables