dictht(字典)

Redis存储结构之dictht字典_distinct

/*
* 哈希表
*/
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 程序;

当程序处理完以上三种情况之后,新的键值对才会被真正地添加到字典上。

Redis存储结构之dictht字典_redis_02

解决hash冲突

在哈希表实现中, 当两个不同的键拥有相同的哈希值时, 称这两个键发生碰撞(collision), 而哈希表实现必须想办法对碰撞进行处理。

字典哈希表所使用的碰撞解决方法被称之为 链地址法: 这种方法使用链表将多个哈希值相同的节点串连在一起, 从而解决冲突问题。

假设现在有一个带有三个节点的哈希表,如下图:

Redis存储结构之dictht字典_哈希算法_03


对于一个新的键值对 ​​key4​​​ 和 ​​value4​​​ , 如果 ​​key4​​​ 的哈希值和 ​​key1​​​ 的哈希值相同, 那么它们将在哈希表的 ​​0​​ 号索引上发生碰撞。

通过将 ​​key4-value4​​​ 和 ​​key1-value1​​ 两个键值对用链表连接起来, 就可以解决碰撞的问题:

Redis存储结构之dictht字典_redis_04