核心原理:哈希表
很多人用 Python 字典觉得快,但未必清楚底层的门道。其实这主要归功于它的底层实现:哈希表(Hash Table)。
简单来说,哈希表就是一种通过**键(key)直接映射到值(value)**存储位置的数据结构。
它是如何工作的?
当你执行类似 d['name'] = 'Alice' 的操作时,Python 内部会经历这几个步骤:
-
计算哈希值 Python 会对键
'name'调用其__hash__()方法,得到一个整数。这个值是确定性的,意味着同一个键每次算出来的结果都一样。这也是为什么不可变类型(如str,int,tuple)能当键,而可变类型(如list,dict)不能的原因。print(hash("name")) # 输出一个固定的整数 -
定位槽位 哈希值通常很大,不能直接当数组下标。Python 会通过取模运算将其映射到哈希表的**槽位(slot)**索引上,公式大概是
index = hash(key) % table_size。 -
存取数据 拿到索引后,Python 直接访问该位置。因为数组索引访问本身就是 O(1) 操作,所以字典的查找、插入和删除在平均情况下也是 O(1)。
遇到哈希冲突怎么办?
不同的键可能产生相同的哈希值,或者映射到同一个索引,这就叫哈希冲突。Python 处理得比较聪明:
- 开放寻址法:如果目标槽位被占用了,它会按特定规则(如线性探测)找下一个空闲位置。这种优化能有效减少'聚集'问题。
- 双重校验:Python 把哈希值、键、值存在一起。查找时先比哈希值,不同就直接跳过;相同再比对键是否相等。这样即便冲突多,速度也很快。
容量不够了会怎样?
哈希表初始大小是固定的,但字典是动态的。当元素太多导致装载因子过高时,冲突概率上升,性能就会下降。这时候 Python 会自动触发扩容:创建一个更大的表,把所有键值对重新哈希过去。虽然扩容有成本,但分摊到每次操作上,依然能保持高效的查询体验。

