Hashdict

Hashdict


宏定义


#define DICT_OK 1
#define DICT_ERR 0

#define dictHashKey(d, key) (d)->type->hashFunction(key)
#define dictSetKey(d, entry, _key_)
#define dictSetVal(d, entry, _val_)
#define dictIsRehashing(d) ((d)->rehashidx != -1)
#define DICT_HT_INITIAL_SIZE     4
#define dictCompareKeys(d, key1, key2) \
    (((d)->type->keyCompare) ? \
        (d)->type->keyCompare((d)->privdata, key1, key2) : \
        (key1) == (key2))
#define dictFreeVal(d, entry) \
    if ((d)->type->valDestructor) \
        (d)->type->valDestructor((d)->privdata, (entry)->v.val)
#define dictFreeKey(d, entry) \
    if ((d)->type->keyDestructor) \
        (d)->type->keyDestructor((d)->privdata, (entry)->key)
#define dictGetVal(he) ((he)->v.val)
static int dict_can_resize = 1;
static unsigned int dict_force_resize_ratio = 5;
//哈希表节点结构
typedef struct dictEntry
{
	void* key;
	union {
		void* val;
		uint64_t u64;
		int64_t s64;
	}v;
	struct dictEntry* next;
}dictEntry;

在这里插入图片描述

//哈希表结构
typedef struct dictht {
	dictEntry** table;
	unsigned long size;
	unsigned long sizemask;
	unsigned long used;
}dictht;

在这里插入图片描述

//字典结构
typedef struct dict {
	dictType* type;
	void* privdata;
	dictht ht[2];
	long rehashidx;
	unsigned long iterators;
}dict;
// dictType 用于操作字典类型函数
typedef struct dictType {
	uint64_t(*hashFunction)(const void* key);	// 计算哈希值的函数
	void* (*keyDup)(void* privdata, const void* key);	// 复制键的函数
	void* (*valDup)(void* privdata, const void* obj);	// 复制值的函数
	int (*keyCompare)(void* privdata, const void* key1, const void* key2);	// 比较键的函数
	void (*keyDestructor)(void* privdata, void* key);	// 销毁键的函数
	void (*valDestructor)(void* privdata, void* obj);	// 销毁值的函数
} dictType;

在这里插入图片描述
字典迭代器:dictlterator

//重置哈希表各属性
static void dictReset(dictht* ht)
{
	ht->table = NULL;
	ht->size = 0;
	ht->sizemask = 0;
	ht->used = 0;
}
//创建字典结构
dict* dictCreate(dictType* type, void* privDataPtr)
{
	dict* d = (dict*)malloc(sizeof(*d));
	dictInit(d, type, privDataPtr);
	return d;
}
//初始化字典结构
int dictInit(dict* d, dictType* type, void* privDataPtr)
{
	dictReset(&d->ht[0]);
	dictReset(&d->ht[1]);
	d->type = type;
	d->privdata = privDataPtr;//需要传给特定类型函数的可选参数
	d->rehashidx = -1;//记录rehash的进度
	d->iterators = 0;//Redis字典自己实现的迭代器,用于遍历,该变量用于记录迭代器的运行数量
	return 1;
}

1)Rehash(渐进式扩展函数)

//渐进式扩展函数
int dictRehash(dict* d, int n)
{
	if (!dictIsRehashing(d))return 0;

	while (n--&&d->ht[0].used!=0)
	{ // n代表需要迁移的索引结点数量
		dictEntry* de, * nextde;
		//确保rehashidx没有越界
		assert(d->ht[0].size > (unsigned)d->rehashidx);
		while (d->ht[0].table[d->rehashidx] == NULL)
		{
			d->rehashidx++;
		}
		// 指向第一个非空索引的链表表头节点
		de = d->ht[0].table[d->rehashidx];
		// 将链表中的所有节点迁移到新哈希表(重新计算位置,在新表上可能就不是在一条链上了)
		while (de)
		{
			unsigned int h;
			// 保存下个节点的指针
			nextde = de->next;
			//计算结点插入的位置索引
			h = dictHashKey(d, de->key) & d->ht[1].sizemask;
			de->next = d->ht[1].table[h];
			d->ht[0].used--;
			d->ht[1].used++;
			de = nextde;
		}
		// 将刚迁移完的哈希表索引的指针设为空
		d->ht[0].table[d->rehashidx] = NULL;
		d->rehashidx++; // 更新 rehash 索引
	}
	free(d->ht[0].table);
	d->ht[0] = d->ht[1];
	dictReset(&d->ht[1]);
	d->rehashidx = -1;
	return 0;
}
//单步Rehash
static void dictRehashStep(dict* d)
{
	//要求该字典上不存在安全迭代器
	if (d->iterators == 0) dictRehash(d, 1);
}

在这里插入图片描述

2)键值对插入函数

//将键插入到字典中
dictEntry* dictAddRaw(dict* d, void* key)
{
	long index; 
	dictEntry* entry;
	dictht* ht;
	if (dictIsRehashing(d)) dictRehashStep(d);
	if ((index = _dictKeyIndex(d, key, dictHashKey(d, key))) == -1)	
		// 获取当前key的hash值,如果已经存在直接返回NULL
		return NULL;
	ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
	entry = (dictEntry *)malloc(sizeof(*entry));// 分配内存空间
	entry->next = ht->table[index];	// 插入到index头部
	ht->table[index] = entry;//此处不是指向,相当于直接赋值,让新插入的节点成为新的头部
	ht->used++;	// 已有节点数加一
	dictSetKey(d, entry, key);
	return entry;
}
//将给定值对添加到字典中
int dictAdd(dict* d, void* key, void* val) // 添加一个键值对进入到dict中
{
	// 尝试添加键到字典,并返回包含了这个键的新哈希节点 T = O(N)
	dictEntry* entry = dictAddRaw(d, key);
	// 键已存在,添加失败
	if (!entry) return DICT_ERR;

	// 键不存在,设置节点的值
	// T = O(1)
	dictSetVal(d, entry, val);

	// 添加成功
	return DICT_OK;
}

在这里插入图片描述
3)索引获取函数

//获取hash index;返回可以将Key插入到哈希表的索引位置
static int dictKeyIndex(dict* d, const void* key)
{
	unsigned int h, idx, table;
	dictEntry* he;//哈希节点
    //如果需要的话,扩展哈希表
	if (dictExpandIfNeeded(d) == DICT_ERR)
	{
		return -1;
	}
	//获取哈希值
	h = dictHashKey(d, key);

	for (table = 0; table <= 1; table++)
	{  
		idx = h & d->ht[table].sizemask; //获取索引值
		he = d->ht[table].table[idx];// 获得哈希表索引上的结点(存在哈希冲突的话,改结点就是链表结点)

		while (he)
		{
			if (dictCompareKeys(d, key, he->key))//比较当前节点的值与给定值是否相等
				return -1;
			he = he->next;//继续找下一个节点
		}
		//程序执行到此处,说明0号哈希表中不存在该节点
		//此时若正在进行Rehash,则在1号哈希表中继续找,否则直接退出循环
		if (!dictIsRehashing(d))
			break;
	}
	return idx;//程序执行到此处,说明在哈希表中不存在该节点,可以放心插入,直接返回新获得的索引值
}

在这里插入图片描述
4)字典哈希表扩展系列函数(非渐进式)

//计算第一个大于等于 size 的 2 的 N 次方,用作新哈希表大小的值
//根据ht[0]的大小,确定rehash操作需要的ht[1]的大小
static unsigned long dictNextPower(unsigned long size)
{
	unsigned long i = DICT_HT_INITIAL_SIZE;

	if (size >= LONG_MAX) return LONG_MAX;
	while (1) {
		if (i >= size)
			return i;
		i *= 2;
	}
}
//字典哈希表的扩展
int dictExpand(dict* d, unsigned long size)
{ // 不能在字典正在 rehash 时进行扩展表操作,size 的值也不能小于 0 号哈希表的当前已使用节点
	if (dictIsRehashing(d) || d->ht[0].used > size)
		return DICT_ERR;
	dictht n;//新哈希表
	unsigned long realsize = dictNextPower(size);
	//确定新哈希表大小
	n.size = realsize;
	n.sizemask = realsize - 1;
	//为1号哈希表分配内存空间
	n.table = (dictEntry**)calloc(realsize ,sizeof(dictEntry*));
	n.used = 0;
	//0号哈希表为空,那么这是一次初始化
	if (d->ht[0].table == NULL)
	{
		d->ht[0] = n;
	} // 0号哈希表不空, 那么这是一次rehash
	else
	{
		d->ht[1] = n;
		d->rehashidx = 0;
	}
	return DICT_OK;
}
//根据需要,初始化字典(的哈希表),或者对字典(的现有哈希表)进行扩展
static int dictExpandIfNeeded(dict* d)
{
	// 渐进式 rehash 已经在进行了,不能扩展和初始化 直接返回
	if (dictIsRehashing(d))
		return DICT_OK;//返回ok

	// 如果字典(的 0 号哈希表)为空,那么创建并返回初始化大小的 0 号哈希表
	if (d->ht[0].size == 0)
		return dictExpand(d, DICT_HT_INITIAL_SIZE);

	// 一下两个条件之一为真时,对字典进行扩展
	//1)字典已使用节点数和字典大小之间的比率接近 1:1,并且 dict_can_resize 为真
	//2)已使用节点数和字典大小之间的比率超过 dict_force_resize_ratio
	if (d->ht[0].used >= d->ht[0].size && (dict_can_resize || d->ht[0].used / d->ht[0].size > dict_force_resize_ratio))
	{
		// 新哈希表的大小至少是目前已使用节点数的两倍
		return dictExpand(d, d->ht[0].used * 2);
	}
	//返回ok标志
	return DICT_OK;
}

5)字典哈希表缩减函数

//缩小字典的哈希表
int dictResize(dict* d)
{

	//新表所需结点的最小数量
	int minimal;
	// 不能在关闭 rehash 或者正在 rehash 的时候调用
	if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;

	// 计算让比率接近 1:1 所需要的最少节点数量
	minimal = d->ht[0].used;
	if (minimal < DICT_HT_INITIAL_SIZE)
		minimal = DICT_HT_INITIAL_SIZE;
	// 调整字典的大小
	return dictExpand(d, minimal);
}

6)哈希表键和值的查找函数(注释与上相同)

//查找键(注释与上类似)
dictEntry* dictFind(dict* d, const void* key)
{
	dictEntry* he;
	unsigned int h, idx, table;
	if (d->ht[0].used == 0)
	{
		return NULL;
	}
	if (dictIsRehashing(d))dictRehashStep(d);
	h = dictHashKey(d, key);//计算哈希值
	for (table = 0; table < 1; table++)
	{
		idx = h & d->ht[table].sizemask;//计算索引值
		he = d->ht[table].table[idx];//获取哈希表索引上的节点
		while (he)
		{
			if (dictCompareKeys(d, key, he->key))
				return he;

			he = he->next;
		}
		if (!dictIsRehashing(d))return NULL;
	}
	return NULL;
}
//查找值
void *dictFetchVslue(dict* d, const void* key)
{
	dictEntry* he;
	//根据键查找在字典中是否存在
	he = dictFind(d, key);
	//若存在返回该节点的值,反之返回NULL
	return he ? dictGetVal(he) : NULL;//dictGetVal为宏定义
}

7)查找并删除给定键的函数(注意:头部节点特殊处理)

//查找并删除给定键的节点
//参数 nofree 决定是否调用键和值的释放函数
//0 表示调用,1 表示不调用
static int dictGenericDelete(dict* d, const void* key, int nofree)
{
	unsigned int h, idx;
	dictEntry* he, * prevHe;
	int table;
	if (d->ht[0].size == 0)
	{
		return DICT_ERR;
	}

	if (dictIsRehashing(d))dictRehashStep(d);
	h = dictHashKey(d, key);
	for (table = 0; table < 1; table++)
	{
		idx = h & d->ht[0].sizemask;
		he = d->ht[table].table[idx];
		prevHe = NULL;
		while (he)
		{
			if (dictCompareKeys(d, key, he->key))
			{
				if (prevHe)
				{//删除头部之后的节点
					prevHe->next = he->next;
				}
				else
				{//删除头部节点
					d->ht[table].table[idx] = he->next;
				}
				//如果需要释放,则调用键和值的释放函数
				if (!nofree)
				{
					dictFreeKey(d, he);
					dictFreeVal(d, he);
				}
				free(he);
				d->ht[table].used--;
				return DICT_OK;
			}
			prevHe = he;
			he = he->next;
		}
		//程序只想到此处,说明在0号哈希表中没有该节点,
		//此时判断是否正在rehash,若没有进行Rehash,则不用再继续找了,否则在1号哈希表中继续找
		if (!dictIsRehashing(d))
			break;
	}
	return DICT_ERR;
}
//删除键
int dictDelete(dict* ht, const void* key)
{
	return dictGenericDelete(ht, key, 0);
}

在这里插入图片描述

8)清空哈希表

//删除哈希表上的所有节点
int dictClear(dict* d, dictht* ht)
{
	unsigned long i;
	for (i = 0; i < ht->size && ht->used>0; i++)
	{
		dictEntry* he, * nextHe;
		if ((he = ht->table[i]) == NULL)continue;
		while (he != NULL)
		{
			nextHe = he->next;
			dictFreeKey(d, he);//删除键
			dictFreeVal(d, he);//删除值
			free(he);
			ht->used--;
			he = nextHe;
		}
	}
	free(ht->table);//释放哈希表结构
	dictReset(ht);//重置哈希表属性
	return DICT_OK;
}

9)清空并释放整个字典

//删除并释放整个字典
void dictRelease(dict* d)
{//删除并释放两个哈希表结构
	dictClear(d, &d->ht[0]);
	dictClear(d, &d->ht[1]);
	//释放字典结构
	free(d);
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值