本文主要翻译自 so 上面的问题 Why can a Python dict have multiple keys with the same hash?Praveen Gollakota 的答案

  • Python 字典是通过哈希表实现的

  • 哈希表必然存在哈希冲突。比如:就算两个键存在相同的哈希值,哈希表必须要有策略用来明确两个值插入和读取

  • Python 字典使用开放寻址法解决哈希冲突(下面展开讲)(源码:dictobject.c:296-297

  • Python 的哈希表仅仅是一块连续的内存(类似于数组,因此可以使用索引进行 O(1) 的查找)

  • 表里的每个插槽只能存储一个 entry,这是很重要的

  • 表里的每个 entry 实际上存储了三个值,这是由 C 结构实现的(详见 dictobject.h:51-56

  • 下面是 Python 哈希表的逻辑示例图,0,1,...,i,... 这些数是对插槽的索引(仅仅只是为了说明,实际上它们并没有与表格一起存放)

    # Logical model of Python Hash table
    -+-----------------+
    0| <hash|key|value>|
    -+-----------------+
    1| ... |
    -+-----------------+
    .| ... |
    -+-----------------+
    i| ... |
    -+-----------------+
    .| ... |
    -+-----------------+
    n| ... |
    -+-----------------+
  • 新字典初始化时拥有 8 个插槽(见 dictobject.h:49

  • 当往哈希表中添加 entry 时,我们以一些插槽开始,比如 i,它是基于对键的哈希。Cpython 使用 i = hash(key) & mask 初始化(这里 mask = PyDictMINSIZE - 1,但这不是重点),注意初始值 i 取决于对键的哈希

  • 如果该插槽是空的,entry 将会被添加到插槽中(entry 即 <hash|key|value>),如果插槽已经被占用时怎么办呢?这常常是由于其它的 entry 拥有相同的哈希值(即哈希冲突)

  • 如果插槽被占用,CPython(包括 PyPy)会对比已占用的和将被插入的 entry 的哈希值和键(使用 == 对比而不是 is)(见:dictobject.c:337,344-345),如果两个都相同,则认为这个 entry 已经存在,继而转向下一个被插入的 entry。如果存在哈希和键中某一个不匹配,则会开始查找

  • 查找意味它会一个一个的查看插槽是否为空,以找到一个空的插槽。技术上来说,我们可以通过不断加 1,如 i+1,i+2,...一旦找到可用的就停止(即线性查找)。但是,因为某些原因(源代码的注释非常漂亮的阐明了这些原因,见 dictobject.c:33-126),CPython 使用了随机查找。在随机查找中,下一个插槽的位置是一个伪随机数,而 entry 也会被添加到找到的第一个空的插槽中。具体的算法对于本次讨论来说并不太重要(具体可以查看 dictobject.c:33-126)。重要的是当第一个空插槽被找到时,查找则停止

  • 同样的事情也发生在索引的时候,它始于初始化的值 i(i 取决于键的哈希值),如果对应的插槽所在的 entry 哈希值和键都不匹配,则会开始查找,直到找到一个匹配的插槽。如果所有的插槽都找遍了也没有找到匹配的,则会报告错误

  • 另外,字典将会在占用了 2/3 的时候重新调整大小,这会避免降低查找的速度(见 dictobject.h:64-65

实际测试效果如下:

class HashTester(object):

    def __init__(self):
self.value = 42 def __hash__(self):
return self.value def __eq__(self, other):
return self.value == other.value class HashTester2(object): def __hash__(self):
return 42
>>> a = HashTester()
>>> b = HashTester()
>>> {a: 'this is a', b: 'this is b'} # a 与 b 的 hash 和 key 都相等
{<__main__.HashTester object at 0x00000222B7A691C0>: 'this is b'} >>> e = HashTester2()
>>> f = HashTester2()
>>> {e: 'this is e', f: 'this is f'} # e 与 f 哈希冲突
{<__main__.HashTester2 object at 0x00000222B7A69CD0>: 'this is e', <__main__.HashTester2 object at 0x00000222B7A690A0>: 'this is f'}

Python 字典是如何解决哈希冲突的的更多相关文章

  1. PAT 甲级 1145 Hashing - Average Search Time (25 分)(读不懂题,也没听说过平方探测法解决哈希冲突。。。感觉题目也有点问题)

    1145 Hashing - Average Search Time (25 分)   The task of this problem is simple: insert a sequence of ...

  2. Java集合(九)哈希冲突及解决哈希冲突的4种方式

    Java集合(九)哈希冲突及解决哈希冲突的4种方式 一.哈希冲突 (一).产生的原因 哈希是通过对数据进行再压缩,提高效率的一种解决方法.但由于通过哈希函数产生的哈希值是有限的,而数据可能比较多,导致 ...

  3. 【面试普通人VS高手系列】HashMap是怎么解决哈希冲突的?

    常用数据结构基本上是面试必问的问题,比如HashMap.LinkList.ConcurrentHashMap等. 关于HashMap,有个学员私信了我一个面试题说: "HashMap是怎么解 ...

  4. Python 字典和集合基于哈希表实现

    哈希表作为基础数据结构我不多说,有兴趣的可以百度,或者等我出一篇博客来细谈哈希表.我这里就简单讲讲:哈希表不过就是一个定长数组,元素找位置,遇到哈希冲突则利用 hash 算法解决找另一个位置,如果数组 ...

  5. 自己动手实现 HashMap(Python字典),彻底系统的学习哈希表(上篇)——不看血亏!!!

    HashMap(Python字典)设计原理与实现(上篇)--哈希表的原理 在此前的四篇长文当中我们已经实现了我们自己的ArrayList和LinkedList,并且分析了ArrayList和Linke ...

  6. 一次电话Java面试的问题总结(JDK8新特性、哈希冲突、HashMap原理、线程安全、Linux查询命令、Hadoop节点)

    面试涉及问题含有: Java JDK8新特性 集合(哈希冲突.HashMap的原理.自动排序的集合TreeSet) 多线程安全问题 String和StringBuffer JVM 原理.运行流程.内部 ...

  7. [Java]HashMap实现与哈希冲突,与HashTable的区别

    对于 Map ,最直观就是理解就是键值对,映射,key-value 形式.一个映射不能包含重复的键,一个键只能有一个值.平常我们使用的时候,最常用的无非就是 HashMap. HashMap 实现了 ...

  8. 数据结构与算法Python版 熟悉哈希表,了解Python字典底层实现

    Hash Table 散列表(hash table)也被称为哈希表,它是一种根据键(key)来存储值(value)的特殊线性结构. 常用于迅速的无序单点查找,其查找速度可达到常数级别的O(1). 散列 ...

  9. 第二百九十六节,python操作redis缓存-Hash哈希类型,可以理解为字典类型

    第二百九十六节,python操作redis缓存-Hash哈希类型,可以理解为字典类型 Hash操作,redis中Hash在内存中的存储格式如下图: hset(name, key, value)name ...

随机推荐

  1. 异常处理截止和UML图

    0.异常处理机制 0.1.java中异常的作用是:增强程序健壮性. 0.2.java中异常以类和对象的形式存在. 1.java的异常处理机制 1.1.异常在java中以类和对象的形式存在.那么异常的继 ...

  2. SpringCloud微服务实战——搭建企业级开发框架(二十六):自定义扩展OAuth2实现短信验证码登录

    现在手机验证码登录似乎是每个网站必备的功能,OAuth2支持扩展自定义授权模式,前面介绍了如何在系统集成短信通知服务,这里我们进行OAuth2的授权模式自定义扩展,使系统支持短信验证码登录. 1.在g ...

  3. 更通俗的理解JS原型链

    最近在网上看到一篇理解原型链的,感觉非常好非常通俗易懂,拿来记录一下~: 1)人是人他妈生的,妖是妖他妈生的.人和妖都是对象实例,而人他妈和妖他妈就是原型.原型也是对象,叫原型对象. 2)人他妈和人他 ...

  4. 洛谷 P5044 - [IOI2018] meetings 会议(笛卡尔树+DP+线段树)

    洛谷题面传送门 一道笛卡尔树的 hot tea. 首先我们考虑一个非常 naive 的区间 DP:\(dp_{l,r}\) 表示区间 \([l,r]\) 的答案,那么我们考虑求出 \([l,r]\) ...

  5. Atcoder Grand Contest 030 F - Permutation and Minimum(DP)

    洛谷题面传送门 & Atcoder 题面传送门 12 天以前做的题了,到现在才补/yun 做了一晚上+一早上终于 AC 了,写篇题解纪念一下 首先考虑如果全是 \(-1\)​ 怎么处理.由于我 ...

  6. DirectX12 3D 游戏开发与实战第十章内容(下)

    仅供个人学习使用,请勿转载.谢谢! 10.混合 本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合.因此,该技术可 ...

  7. 最短剩余时间优先法则SRTN

  8. 巩固javaweb第十三天

    巩固内容: HTML 表格 表格由 <table> 标签来定义.每个表格均有若干行(由 <tr> 标签定义),每行被分割为若干单元格(由 <td> 标签定义).字母 ...

  9. A Child's History of England.22

    CHAPTER 8 ENGLAND UNDER WILLIAM THE FIRST, THE NORMAN CONQUEROR Upon the ground where the brave Haro ...

  10. UBI 文件系统之分区挂载

    Linux 系统中有关mtd和ubi的接口:(1) cat /proc/mtd:可以看到当前系统的各个mtd情况,(2) cat /proc/partitions: 分区信息,有上面的类似(3) ca ...