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 ...
随机推荐
- 如何将Android的AOSP仓库放置到自己的gitlab服务器上?
平台 Ubuntu 18.04 GitLab Community Edition 11.11.0 参考 https://source.android.google.cn/ 概述 git ...
- 后台返回的base64的图片格式,前端如何转为普通的图片格式
在上一篇的博客当中,写了前端如何将普通的图片格式转为base64的图片,今天开发的时候遇到了后台返回的图片格式是base64的,我这边需要把base64的图片格式转为普通的,搜了一下js的方法,感觉很 ...
- C#通过SendARP()获取WinCE设备的Mac网卡物理地址
ARP(Address Resolution Protocol) 即 地址解析协议,是根据IP地址获取物理地址的一个TCP/IP协议. SendARP(Int32 dest, Int32 host, ...
- 【转】Linux 网络工具详解之 ip tuntap 和 tunctl 创建 tap/tun 设备
原文:https://www.cnblogs.com/bakari/p/10449664.html -------------------------------------------------- ...
- Elasticsearch 待办
日期格式:yyyy-MM-dd,改为 yyyy-MM-dd HH:mm:ss.SSS:实体类路径:https://github.com/cag2050/spring_boot_elasticsearc ...
- Nuxt.js 提供了两种发布部署应用的方式:服务端渲染应用部署 和 静态应用部署
官方网址:https://zh.nuxtjs.org/guide/commands/#%E5%8F%91%E5%B8%83%E9%83%A8%E7%BD%B2
- 17、Python面向对象高级
一.isinstance和issubclass type():不会认为子类实例是一种父类类型: isinstance():认为子类实例是一种父类类型. issubclass():判断是否为其子类. c ...
- wordpress在首页列出所有分类及分类下的文章
前面我们谈了wordpress调用指定分类文章,如果想要调用所有分类的文章如何实现呢?比如在wordpress首页列出所有分类及分类下的文章,其实方法类似 <?php $cats = get_c ...
- Numpy | 14 字符串函数
本章函数用于对 dtype 为 numpy.string_ 或 numpy.unicode_ 的数组执行向量化字符串操作. 它们基于 Python 内置库中的标准字符串函数. 这些函数在字符数组类(n ...
- node.js封装数据库增删改查
数据库增删改查的封装 小编不容易 const sql = { insert: function (Collection, insertData) { return new Promise((resol ...