哈希表
哈希表是一种典型的以空间换取时间的数据结构,在没有冲突的情况下,对任意元素的插入、索引、删除的时间复杂度都是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 哈希表的更多相关文章

  1. [PHP内核探索]PHP中的哈希表

    在PHP内核中,其中一个很重要的数据结构就是HashTable.我们常用的数组,在内核中就是用HashTable来实现.那么,PHP的HashTable是怎么实现的呢?最近在看HashTable的数据 ...

  2. Java 哈希表运用-LeetCode 1 Two Sum

    Given an array of integers, find two numbers such that they add up to a specific target number. The ...

  3. ELF Format 笔记(十五)—— 符号哈希表

    ilocker:关注 Android 安全(新手) QQ: 2597294287 符号哈希表用于支援符号表的访问,能够提高符号搜索速度. 下表用于解释该哈希表的组织,但该格式并不属于 ELF 规范. ...

  4. Java基础知识笔记(一:修饰词、向量、哈希表)

    一.Java语言的特点(养成经常查看Java在线帮助文档的习惯) (1)简单性:Java语言是在C和C++计算机语言的基础上进行简化和改进的一种新型计算机语言.它去掉了C和C++最难正确应用的指针和最 ...

  5. 什么叫哈希表(Hash Table)

    散列表(也叫哈希表),是根据关键码值直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. - 数据结构 ...

  6. 【哈希表】CodeVs1230元素查找

    一.写在前面 哈希表(Hash Table),又称散列表,是一种可以快速处理插入和查询操作的数据结构.哈希表体现着函数映射的思想,它将数据与其存储位置通过某种函数联系起来,其在查询时的高效性也体现在这 ...

  7. openssl lhash 数据结构哈希表

    哈希表是一种数据结构,通过在记录的存储位置和它的关键字之间建立确定的对应关系,来快速查询表中的数据: openssl lhash.h 为我们提供了哈希表OPENSSL_LHASH 的相关接口,我们可以 ...

  8. Berkeley DB的数据存储结构——哈希表(Hash Table)、B树(BTree)、队列(Queue)、记录号(Recno)

    Berkeley DB的数据存储结构 BDB支持四种数据存储结构及相应算法,官方称为访问方法(Access Method),分别是哈希表(Hash Table).B树(BTree).队列(Queue) ...

  9. python数据结构与算法——哈希表

    哈希表 学习笔记 参考翻译自:<复杂性思考> 及对应的online版本:http://greenteapress.com/complexity/html/thinkcomplexity00 ...

随机推荐

  1. 目标检测论文解读4——Faster R-CNN

    背景 Fast R-CNN中的region proposal阶段所采用的SS算法成为了检测网络的速度瓶颈,本文是在Fast R-CNN基础上采用RPN(Region Proposal Networks ...

  2. Apache JMeter系列.1

    最爱看统计 --01-- 简介 Apache JMeter Apache JMeter可用于测试静态和动态资源(文件,Servlet,Perl脚本,Java对象,数据库和查询,FTP服务器等)的性能. ...

  3. version GLIBCXX3.4.21 not defined in file libstdc++.so.6 with link time reference

    问题:在运行C++程序时,输入 ./a.out 输出:symbol _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev, version ...

  4. windows命令查看端口占用情况

    打开cmd 查看端口占用情况:netstat -aon 查看PID对应的进程:tasklist /FI "PID eq 16948"

  5. DOM是浏览器提供给开发者的语柄、套接字、文件接口

    DOM是浏览器提供给开发者的语柄.套接字.文件接口

  6. Python 鼠标键盘操作

    1.鼠标操作 from pymouse import PyMouse myMouse = PyMouse() #获取当前的鼠标位置 # nowP = myMouse.position() # prin ...

  7. PHP字符串比较函数详解

    在PHP中,对于字符串之间的比较有很多种方法,第一种是使用 strcmp()函数和 strcasecmp()函数按照字节进行比较,第二种是使用 strnatcmp()函数按照自然排序法进行比较,第三种 ...

  8. Edraw Max 9.4 Crack Method

    使用010editor修改以下两个文件. BaseCore.dll (修改二进制内容hex) Before C6 45 C8 62 C6 45 C9 64 C6 45 CA 65 C6 45 CB 6 ...

  9. 做个小插件(打开Part路径插件)

    1 (CAIDAN.men) VERSION EDIT UG_GATEWAY_MAIN_MENUBAR AFTER UG_HELP CASCADE_BUTTON TOOLS LABEL 工具 END_ ...

  10. 洛谷 CF1153B Serval and Toy Bricks

    目录 题目 思路 \(Code\) 题目 CF1153B Serval and Toy Bricks 思路 自己也很懵的一道题(不知道自己怎么就对了)...只要对于所给的俯视图上值为\(1\)的位置输 ...