再次解释一下 Python 字典使用的哈希表

我们在上一篇文章中分析了 CPython 中是如何实现 dict 的。由于涉及内容较多,我可能并没有描述清楚 dict 所用的哈希表的具体结构。

如果你也对此有疑问,不妨来看一下本文对哈希表结构的进一步分析。


【dict 哈希表】

先看一张重新制作的图。

这张图描述的就是 CPython 中 PyDictKeysObject 结构中的成员变量 dk_indices。它的数据类型是 char[],表明它是一块连续分配的内存。

dk_indices 在使用中被分割为两部分。

第一部分是图中的 Slots 数组,也就是我们之前文章中提到的哈希表索引内存块;第二部分是 KeyValues 数组,我们之前称之为哈希表键值对内存块。这两部分都是连续的内存,都可以当做数组来处理,从而能充分利用数组随机访问的特性,提高 dict 的访问效率。


Slots 数组有两个作用:

  1. 根据字典中元素 key 的哈希值为元素提供一个槽位(slot)。这是哈希表最一般的用途。

  2. Slots 中存储的并非元素本身,而是元素在 KeyValues 数组中的索引。由于 Slots 本身就是一个数组,通过 Slots[KeySlot] 就可以直接获取元素在 KeyValues 数组中的位置,效率很高。

图中的 slot 有 3 种取值:-1、-2 和自然数。分别代表着 slot 的三种状态:

  1. 未曾使用(UnUsed)

    这是每个 slot 的初始状态,表示 slot 从未被 key 命中过,从未存储过任何元素的索引。

  2. 回收备用(Dummy)

    这类 slot 已存储过元素的索引,但元素已被删除,被标记为可再次使用。从这里也可以看到,执行删除操作时,哈希表并不会真正删除元素。这既能提高内存的使用效率,又可以避免物理删除导致的其他元素访问失效。

  3. 活动的(Active)

    slot 中存储的是一个有效的元素的索引。

哈希表的 hash 算法和冲突算法都是运行在这个 Slots 数组上的。

计算 slot 的基本方法为:

slot = hash(key) & (dk_size - 1)

当发生 key 冲突时,会采用如下算法在 Slots 中查找下一个合适的 slot:

const size_t mask = DK_MASK(keys); // 计算初始 slot    size_t i = hash & mask; //获取 slot 中的索引值 Py_ssize_t ix = dictkeys_get_index(keys, i); //循环检查索引值是否有效 for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dictkeys_get_index(keys, i); }

KeyValues 数组用来存储键值对。键值对是以追加的方式添加到数组的尾部的,添加完成之后,将其位置索引保存在 key 对应的 slot 中。

dict.items()、dict.keys() 和 dict.values() 这三个方法就是直接访问 KeyValues 数组的,因而元素的输出顺序和插入顺序是一致的。


【使用这种结构的好处】

首先,使用连续的内存可充分利用数组随机访问的特性,保证了 dict 的访问效率。

其次,Slots 数组只保存元素的索引,每个 slot 只占用少量固定长度的内存,可大大节省内存空间。

我们可以想象,当 dict 比较大时,如果其中只包含少量有效的(Active) slot,此时的 Slots 就是一个稀疏的数组。如果 Slots 中存储的是元素对象,每个元素对象的 size 要远大于一个 int 索引值占用的空间,这个稀疏数组会造成比较严重的内存浪费。

因此,CPython 所采用的这种哈希表内存结构的确是一种高效紧凑的数据存储方式,具有很高的时间和空间效率。


【相关文章】

  1. 快速查找,插入有序,从源码分析 Python 底层如何实现字典的这些特性

  2. RealPython 基础教程:Python 字典用法详解

我埋头写作,就像在努力爬坡

你在看和分享,无形中推了我一把

多希望你可以一直关注着我

那是我不懈前行的力量

(0)

相关推荐

  • [PHP] PHP数组的哈希表实现

    [PHP] PHP数组的哈希表实现

  • 深入浅出java的Map

    优质文章,第一时间送达 作者 |  palapala 来源 |  urlify.cn/7RNfIn 一.SpringBoot比较重要的回调机制 HashMap的组成 首先了解数组和链表两个数据结构 1 ...

  • 用 Jupyter 学习 Python 字典 | Linux 中国

    原创 邀你一起成为开源贡献者 Linux中国 1周前   导读:字典数据结构可以帮助你快速访问信息. 本文字数:2152,阅读时长大约:3分钟 https://linux.cn/article-132 ...

  • 再次解释甲状腺有问题,能不能打新冠疫苗!

    这两天广州新增5例本土无症状新冠病例,使得大家对接种新冠疫苗的热情一度高涨.然而,很多有相关疾病的人群,很是但心,他们有某种疾病,到底能不能接种疫苗.尤其是甲状腺疾病的患者. 关于甲状腺有问题的人群, ...

  • python字典

    近些年最长的五一小长假结束了,结束了,结束了...... 来一张照片抚慰一下想出去浪的心...... 字典在python中也是一种常用的数据类型. 它是一种可变容器模型,可用来存储任意类型的对象,如: ...

  • 使用纯 Python 代码来模拟实现 Python 字典

    在前面几篇文章中,我们一起了解了 Python 字典的概念.用法和实现原理. 今天,我们试着用 Python 代码来实现一个具有全功能的字典类,从而加强理解. [基本思路] 首先,我们要确认字典应具备 ...

  • 第11天:Python 字典

    Python 中的字典提供了一种灵活的访问和组织数据的方式 字典是由很多值组成的集合 字典的索引可以是不同的数据类型,同样也不止是整数,也有字符串 字典的索引被称为"键",键及键所 ...

  • python字典常见用法总结

    Python字典是另一种可变容器模型,且可存储任意类型对象,如字符串.数字.元组等其他容器模型. 一.创建字典 字典由键和对应值成对组成.字典也被称作关联数组或哈希表.基本语法如下: 注意: 每个键与 ...

  • RealPython 基础教程:Python 字典用法详解

    在连续编写了5篇和 list 相关的文章之后,我们继续<RealPython 基础教程>这个系列. 今天,我们要学习的数据结构是字典(dict). dict 是一个包含若干对象的集合.它和 ...

  • 技巧 | Python 字典用法详解(超全)

    原创 欧King Python当打之年 1周前 本期导读 字典(Dictionary)是Python提供的一种常用的数据结构,它用于存放具有映射关系的数据.本期给大家带来Python字典11个方法的全 ...

  • 康熙字典起名用字大全及解释,康熙字典取名用字大全

    康熙字典取名用字吉凶分析,康熙字典起名用字大全及解释,康熙字典取名用字大全 在取名的时候大家都是会如何去取名的呢?有些人都是直接利用到康熙字典来去给自己的孩子取名的,这里就和谢咏老师一起来看看康熙字典 ...