3、字典介绍
字典也称为符号表(symbol table)、关联数组(associative array)或者映射(map)是用于保存键值对(key-value pair)的抽象数据结构。
字典中每个键都是唯一的,程序可以在字典中根据键查询与之关联的值(更新、删除)等。
Redis使用字典作为底层实现,对数据库增、删、改、查操作也是构建在字典的操作之上的。
字典是哈希键的底层实现之一。
1、字典实现
哈希表有dict.h/dictht结构定义:
typedef struct dictht{
//哈希表数组
dictEntry **table;
//哈希表大小
unsigned long size;
//哈希表大小掩码,用于计算索引值,总是等于size-1
unsigned long sizemask;
//该哈希表已有节点的数量
unsigned long used;
}
![](https://img2018.cnblogs.com/blog/590264/201810/590264-20181008210932273-689117527.png)
主要介绍:
table是一个数组,每个值都是dictEntry元素
sizemask这个属性与哈希表一起决定一个键应该被放在table数组的哪个索引上面(hashCode/sizemark=index)
2、哈希节点
结构:
typedef struct dictEntry{
//键
void *key;
//值
union{
void *val;
uint64_t u64;
int64_t s64;
} v;
//指向下个哈希表节点,形成链表
struct dictEntry *next;
} dictEntry;
主要介绍:
v属性保存着键值对中值,其中键值对的值可以是一个指针、或者一个uint64_t的整数、或者一个int64_t的整数。
next属性是指向另一个哈希表节点的指针,这个指针可以将多个哈希值相同的键值对链接在一起,以此解决键冲突的问题。
![](https://img2018.cnblogs.com/blog/590264/201810/590264-20181008210950549-1447648318.png)
3、字典
字典有dict.h/dict结构表示:
typedef struct dict{
//类型特定函数
dictType *type;
//私有数据
void *privdata;
//哈希表
dictht ht[2];
//rehash索引
//当rehash不在进行时,值为-1
int trehashidx;
} dict;
重点说明:
ht[2]中字典表只能使用ht[0]哈希表,ht[1]哈希表只会对ht[0]哈希表进行rehash时使用。
trehashidx记录rehash目前进度,如果没有进行rehash则为-1。
type属性与privdate属性是针对不同类型的键值对,为创建多态字典而设置的。
Redis会为不同用途的字典设置不同的类型特点函数。
privdate属性则保存了需要传递给那些特定函数的可选参数。
dictType类型说明:
typedef struct dictType{
//计算哈希值的函数
unsigned int (*hashFunction);
//复制键的函数
void *(*keyDup);
//复制值得函数
void *(*valDup);
//对比键的函数
int (*keyCompare);
//销毁键的函数
void (*keyDestructor);
//销毁值的函数
void (*valDestructor);
} dictType;
![](https://img2018.cnblogs.com/blog/590264/201810/590264-20181008211023072-509420855.png)
4、哈希算法
对键值对中的键进行hash,hash算法根据dict中设置的dictType下的hashFunction进行hash,可以理解为:
hash=dict->dictType->hashFunction(key);
index=hash & dict->ht[0].sizemask;
可以理解为:如果hash=8,则index=8&3=0,
8的二进制位1000,3的二进制位0011,进行&操作后为0.
5、解决键冲突
当两个或者以上数量的键被分配到了相同的哈希表数组的同一个索引上时,称为键发生了冲突。
Redis解决冲突的方式是链地址法,每个数组元素后增加单链表解决。
![](https://img2018.cnblogs.com/blog/590264/201810/590264-20181008211049149-500877307.png)
6、rehash介绍
哈希表在增加或者删除键值对时,为防止哈希表保存的键值对过多或者多少,会对哈希表进行收缩或者扩容。
收缩或者扩容都是有rehash操作来完成的。
rehash步骤:
1、为字典ht[1]哈希表进行分配空间,
如果执行扩容操作,那么ht[1]的大小为第一个大于等于ht[0].used*2的(2的n次方幂)
如果进行收缩操作,则ht[1]的大小为的第一个小于等于ht[0].used*2的(2的n次方幂)
2、将ht[0]哈希表中的键值对rehash到ht[1]上(rehash需要重新计算哈希值和数组索引值),然后将键值对放到ht[1]哈希表的指定位置上。
3、将ht[0]哈希表的值都迁移到ht[1]后,会清空ht[0]哈希表,在将ht[1]设置为ht[0],在将ht[1]新创建一个空白的哈希表。
收藏文章数量从多到少与“把书读薄”是一个道理