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 ...
随机推荐
- rest framework 之路由系统
一.自定义路由 1.urls.py from django.conf.urls import url, include from web.views import s11_render urlpatt ...
- 2019年杭电多校第三场 1008题Game(HDU6610+带修改莫队+Nim博弈)
题目链接 传送门 题意 给你\(n\)堆石子,每堆有\(a_i\)堆石子,\(q\)次操作: 在\([L,R]\)内有多少个子区间使得\(Alice\)(先手)在\(Nim\)博弈中获胜: 交换\(a ...
- python应用-已知三角形的边长求他的面积和周长
""" 已知三角形的边长求他的面积和周长 Author:罗万财 Date:2017-3-3 """ import math a=float( ...
- 第3章 Spring AOP
3.1 Spring AOP简介 3.11什么是AOP? AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程).它是面向对象编程(OOP)的一种补充 ...
- base64文件隐写脚本
base64文件隐写脚本 base64 可以在文件中隐藏信息,记录一下提取脚本 ''' base64文件隐写脚本 import re import base64 b64chars = 'ABCDEFG ...
- vim命令(转)
1.Linux下创建文件 vi test.txt 或者 vim test.txt 或者 touch test.txt 2.vi/vim 使用 基本上 vi/vim 共分为三种模式,分别是命令模式(Co ...
- 发布jar包到远端github仓库使用(将github仓库当作maven仓库)
今天把单点登陆的core模块搬到了github仓库 并且利用github仓库作为maven仓库 在项目中进行了引用 1. 起初看技术博客没有完全引入进来,调整了一下OK了 2. 还可以将其他模块或者工 ...
- three.js 加载3DS 404 文件找不到
web.config修改如下: code: <?xml version="1.0" encoding="utf-8"?> <!-- 有关如何配 ...
- [RN] React Navigation 使用中遇到的显示 问题 汇总
React Navigation 使用中遇到的显示 问题 汇总 https://www.jianshu.com/p/8b1f18affc5d
- javaScript 判断为false
JavaScript把null.undefined.0.NaN和空字符串''视为false,其他值一概视为true