dictht(字典)
/*
* 哈希表
*/
typedef struct dictht {
// 哈希表节点指针数组(俗称桶,bucket)
dictEntry **table;
// 指针数组的大小
unsigned long size;
// 指针数组的长度掩码,用于计算索引值
unsigned long sizemask;
// 哈希表现有的节点数量
unsigned long used;
} dictht;
table
属性是个数组, 数组的每个元素都是个指向 dictEntry
结构的指针。
每个 dictEntry
都保存着一个键值对, 以及一个指向另一个 dictEntry
结构的指针:
/*
* 哈希表节点
*/
typedef struct dictEntry {
// 键
void *key;
// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
// 链往后继节点
struct dictEntry *next;
} dictEntry;
next
属性指向另一个 dictEntry
结构, 多个 dictEntry
可以通过 next
指针串连成链表, 从这里可以看出, dictht
使用链式寻址法来解决hash冲突: 当多个不同的键拥有相同的哈希值时,哈希表用一个链表将这些键连接起来。
添加键值对
根据字典所处的状态, 将给定的键值对添加到字典可能会引起一系列复杂的操作:
- 如果字典为未初始化(即字典的 0 号哈希表的
table
属性为空),则程序需要对 0 号哈希表进行初始化; - 如果在插入时发生了键碰撞,则程序需要处理碰撞;
- 如果插入新元素,使得字典满足了 rehash 条件,则需要启动相应的 rehash 程序;
当程序处理完以上三种情况之后,新的键值对才会被真正地添加到字典上。
解决hash冲突
在哈希表实现中, 当两个不同的键拥有相同的哈希值时, 称这两个键发生碰撞(collision), 而哈希表实现必须想办法对碰撞进行处理。
字典哈希表所使用的碰撞解决方法被称之为 链地址法: 这种方法使用链表将多个哈希值相同的节点串连在一起, 从而解决冲突问题。
假设现在有一个带有三个节点的哈希表,如下图:
对于一个新的键值对 key4
和 value4
, 如果 key4
的哈希值和 key1
的哈希值相同, 那么它们将在哈希表的 0
号索引上发生碰撞。
通过将 key4-value4
和 key1-value1
两个键值对用链表连接起来, 就可以解决碰撞的问题: