Sword 哈希表
哈希表
哈希表是一种典型的以空间换取时间的数据结构,在没有冲突的情况下,对任意元素的插入、索引、删除的时间复杂度都是O()。
这样优秀的时间复杂度是通过将元素的key值以hash方法f映射到哈希表中的某一个位置来访问记录来实现的,即键值为key的元素
必定存储在哈希表中的f(key)的位置。当然,不同的元素的hash值可能相同,这就是hash冲突,有两种解决方法(分离链表发和开
放地址发),ngx采用的是开放地址法. 分离链表法是通过将冲突的元素链接在一个哈希表外的一个链表中,这样,找到hash表中的位置后,就可以通过遍历这个单链表来找到这个元素 开放地址法是插入的时候发现自己的位置f(key)已经被占了,就向后遍历,查看f(key)+1的位置是否被占用,如果没被占用,就占用它,
否则继续相后,查询的时候,同样也如果f(key)不是需要的值,也依次向后遍历,一直找到需要的元素。
哈希表的本质
普通哈希表的查找比较简单,思想就是先根据hash值找到对应桶,然后遍历这个桶的每一个元素,逐字匹配是否关键字完全相同,
完全相同则找到,否则继续,直至找到这个桶的结尾(value = NULL)。
nginx的hash表是固定元素长度的,就是一开始已知所有的键值对。无法动态添加,但是可以修改值

gtc_hash_t * gtc_internal_hash_build(unsigned int max_bucket_count
, unsigned int max_bucket_size
, gtc_pool_t *pool
, gtc_hash_key_t *names
, unsigned int nelts)
{
gtc_hash_t * hash = NULL;
unsigned int n = , i = ;
unsigned int *test = NULL;
unsigned int bucket_size = , start = , index = ;
unsigned int key = , len = ;
unsigned char *elts = NULL;
gtc_hash_elt_t **buckets = NULL;
gtc_hash_elt_t *elt = NULL; //1.校验哈希表初始化参数
if ( == max_bucket_count)
{
//哈希表桶的数目不可以是0
return NULL;
} if (GTC_MAX_BUCKET_SIZE - GTC_CACHELINE_SIZE < max_bucket_size)
{
//哈希表桶的大小必须小于 GTX_MAX_BUCKET_SIZE
/*
为啥要小于 GTX_MAX_BUCKET_SIZE - GTC_CACHELINE_SIZE?
这是为了内存对齐,因为hash表所有的内存都在内存池上
*/
return NULL;
} /*
设计说明:
下面操作的目的是为了确认 需要建立哈希表的每一个元素所占内存空间都必须小于 哈希表中桶的大小
hinit->bucket_size < GTC_HASH_ELT_SIZE(&names[n]) + sizeof(void *)说明
hinit->bucket_size 桶的大小
GTC_HASH_ELT_SIZE(&names[n]) 一个哈希元素的大小
sizeof(void *) 哈希表中每个桶都是以NULL结尾
因此 这个 if 判断的意义是 最起码保证 一个桶可以装一个元素(一个桶可以装多个元素,当然更好)
*/
for (n = ; n < nelts; n++)
{
if (max_bucket_size < GTC_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
{
return NULL;
}
} do
{
//2.计算出桶的数量
test = (unsigned int *)calloc(max_bucket_count, sizeof(unsigned int));
if (NULL == test)
{
break;
} /*
设计说明:
a. bucket_size = hinit->bucket_size - sizeof(void *);说明
bucket_size 为实际可以存放元素的大小
hinit->bucket_size - sizeof(void *) 因为每个桶都是NULL结尾,所以实际大小需要 - sizeof(void *) b. start = nelts / (bucket_size / (2 * sizeof(void *))) + 1;
2 * sizeof(void *) 这个是 gtc_hash_elt_t 最小的值,根据 结构体对齐规则 gtc_hash_elt_t最小就是 2 * sizeof(void *) 大小
bucket_size / (2 * sizeof(void *) 这是桶里最多可以装 多个元素
nelts / (bucket_size / (2 * sizeof(void *))) 计算出最少需要多少个桶
+ 1 为什么需要加1,因为 nelts / (bucket_size / (2 * sizeof(void *))) 本质上是向下取整,最少也应该有一个,所以应该向上取整 */
bucket_size = max_bucket_size - sizeof(void *); //bucket_size 为实际可以存放元素的大小
start = nelts / (bucket_size / ( * sizeof(void *))) + ;
//优化start
if (max_bucket_count > && nelts && max_bucket_count / nelts < )
{
start = max_bucket_count - ;
} //计算出实际桶的数量
for (index = start; index < max_bucket_count; index++)
{
//部分清零策略,提高效率
memset(test, 0x00, (index + ) * sizeof(unsigned int));
for (n = ; n < nelts; n++)
{
if (NULL == names[n].key.data)
{
//不处理空数据
continue;
} //计算当前元素所在桶的位置
key = names[n].key_hash % index;
//计算当前桶的大小
len = test[key] + GTC_HASH_ELT_SIZE(&names[n]);
if (len > bucket_size)
{
//如果当前长度超过桶的最大长度,说明桶的数目不够,需要更加离散
break;
}
test[key] = len;
} if (n == nelts)
{
//已经找到最合适的桶的数目
break;
} continue;
} if (index == max_bucket_count)
{
//当前桶的数量太少,不够存放所有的数据
break;
} //将每个桶最后的NULL 补上
for (i = ; i < index; i++)
{
test[i] += sizeof(void *);
} //计算哈希表的总长度,分配内存空间
len = ;
for (i = ; i < index; i++)
{
if (sizeof(void *) == test[i])
{
/*
设计说明:
空桶直接不会分配内存,因为哈希表元素固定且不支持添加
*/
continue;
}
//数字对齐,加快内存检索
test[i] = gtc_align(test[i], GTC_CACHELINE_SIZE);
len += test[i];
} //创建桶链表
/*
设计说明:
桶里面存储的是 元素的指针,并非元素本身
buckets 的个数一定是 index 个,但是不是每个Index里都有元素的
*/
buckets = gtc_pcalloc(pool, index * sizeof(gtc_hash_elt_t *));
if (NULL == buckets)
{
break;
}
//申请元素内存空间,所有的元素被放置在一块连续的内存上
elts = gtc_pcalloc(pool, len);
if (NULL == elts)
{
break;
} //指针内存对齐
elts = gtc_align_ptr(elts, GTC_CACHELINE_SIZE);
for (i = ; i < index; i++)
{
if (sizeof(void *) == test[i])
{
//空桶,跳过
continue;
}
buckets[i] = (gtc_hash_elt_t *)elts;
elts += test[i];
} //清空探测器,此时test[i] 实际上是 i 这个桶的偏移量
memset(test, 0x00, index * sizeof(unsigned int)); //将元素一一赋值
for (n = ; n < nelts; n++)
{
if (NULL == names[n].key.data)
{
//空元素不管
continue;
}
//找到桶的位置
key = names[n].key_hash % index;
//找到一个元素块
elt = (gtc_hash_elt_t *)((unsigned char *)buckets[key] + test[key]);
//浅拷贝 value
elt->value = names[n].value;
elt->len = (unsigned short)names[n].key.len;
//字符串拷贝
gtc_strlow(elt->name, names[n].key.data, names[n].key.len);
//更新当前桶偏移
test[key] = (unsigned short) (test[key] + GTC_HASH_ELT_SIZE(&names[n]));
} for (i = ; i < index; i++)
{
if (NULL == buckets[i])
{
//空桶不处理
continue;
} elt = (gtc_hash_elt_t *)((unsigned char *)buckets[i] + test[i]); //每个桶最后一个元素是 NULL
elt->value = NULL;
} //哈希表赋值
hash = (gtc_hash_t *)gtc_pcalloc(pool, sizeof(gtc_hash_t));
if (NULL == hash)
{
break;
}
hash->buckets = buckets;
hash->size = index; } while (); //资源释放
if (test)
{
free(test);
test = NULL;
} return hash;
}
Sword 哈希表的更多相关文章
- [PHP内核探索]PHP中的哈希表
在PHP内核中,其中一个很重要的数据结构就是HashTable.我们常用的数组,在内核中就是用HashTable来实现.那么,PHP的HashTable是怎么实现的呢?最近在看HashTable的数据 ...
- Java 哈希表运用-LeetCode 1 Two Sum
Given an array of integers, find two numbers such that they add up to a specific target number. The ...
- ELF Format 笔记(十五)—— 符号哈希表
ilocker:关注 Android 安全(新手) QQ: 2597294287 符号哈希表用于支援符号表的访问,能够提高符号搜索速度. 下表用于解释该哈希表的组织,但该格式并不属于 ELF 规范. ...
- Java基础知识笔记(一:修饰词、向量、哈希表)
一.Java语言的特点(养成经常查看Java在线帮助文档的习惯) (1)简单性:Java语言是在C和C++计算机语言的基础上进行简化和改进的一种新型计算机语言.它去掉了C和C++最难正确应用的指针和最 ...
- 什么叫哈希表(Hash Table)
散列表(也叫哈希表),是根据关键码值直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. - 数据结构 ...
- 【哈希表】CodeVs1230元素查找
一.写在前面 哈希表(Hash Table),又称散列表,是一种可以快速处理插入和查询操作的数据结构.哈希表体现着函数映射的思想,它将数据与其存储位置通过某种函数联系起来,其在查询时的高效性也体现在这 ...
- openssl lhash 数据结构哈希表
哈希表是一种数据结构,通过在记录的存储位置和它的关键字之间建立确定的对应关系,来快速查询表中的数据: openssl lhash.h 为我们提供了哈希表OPENSSL_LHASH 的相关接口,我们可以 ...
- Berkeley DB的数据存储结构——哈希表(Hash Table)、B树(BTree)、队列(Queue)、记录号(Recno)
Berkeley DB的数据存储结构 BDB支持四种数据存储结构及相应算法,官方称为访问方法(Access Method),分别是哈希表(Hash Table).B树(BTree).队列(Queue) ...
- python数据结构与算法——哈希表
哈希表 学习笔记 参考翻译自:<复杂性思考> 及对应的online版本:http://greenteapress.com/complexity/html/thinkcomplexity00 ...
随机推荐
- Django 之 Form 组件
常用功能 From 组件主要有以下几大功能: 生成 HTML 标签 验证用户数据(显示错误信息) HTML Form 提交保留上次提交数据 初始化页面显示内容 小试牛刀 下面我们通过 Form 组件来 ...
- Bag of Tricks for Image Classification with Convolutional Neural Networks笔记
以下内容摘自<Bag of Tricks for Image Classification with Convolutional Neural Networks>. 1 高效训练 1.1 ...
- js-事件1
本课我将讲述js中的事件及一些浏览器兼容问题 本章主要从以下几个方面讲起:1.事件流 2.事件的浏览器兼容 3.鼠标,键盘事件 1. 事件流 什么叫事件流? 描述的是事件接受的顺序.这句话听起来 ...
- AtCoder Beginner Contest 146解题报告
题目地址 https://atcoder.jp/contests/abc146/tasks 感觉没有什么有意思的题... 题解 A #include <bits/stdc++.h> usi ...
- 201671030106 何启芝 实验十四 团队项目评审&课程学习总结
项目 内容 这个作业属于哪个课程 >>2016级计算机科学与工程学院软件工程(西北师范大学) 这个作业的要求在哪里 >>实验十四 团队项目评审&课程学习总结 课程学习目 ...
- python应用-表格式输出一组数据
def main(): names=['关羽','张飞','赵云','马超','貂蝉'] subjects=['语文','数学','Python'] table=[[0 for _ in range( ...
- BZOJ - 3242 :快餐店 (基环树DP) 最小化半径
题意:给定N点N边的无向连通图,现在让你在图中找一点作为餐厅,使得最远点距离这点最近. 思路:为了保留整数,我们求最小直径,最后去除2. 直径来源于两部分: 1,在外向树中: 那么就是树的直接,一棵 ...
- Ofbiz项目学习——阶段性小结——更新数据
一.根据一个字段进行修改 /** * 根据一个字段进行修改(这个条件字段可以是主键, 也可以不是主键) * @param dctx * @param context * @return */ publ ...
- java语言评价--java帝国
“陛下您想想,我们有很多宝贝,” IO大臣根本不理线程大臣, 继续侃侃而谈:“ 比如IoC, AOP,反射.动态代理.泛型.注解.JDBC.JMS...... 还有我们引以为豪的JVM.这些东西,那些 ...
- 由PPPOE看Linux网络协议栈的实现
http://www.cnblogs.com/zmkeil/archive/2013/05/01/3053545.html 这个标题起得比较纠结,之前熟知的PPPOE是作为PPP协议的底层载体,而实际 ...