为什么手动实现一个哈希表

场景

一直用着 map,unordered_map,但是 map 在 malloc_default_zone 分配内存,无法指定一个内存缓冲池给他,同时还有很多代码在 malloc_default_zone 内分配内存,有个内存泄漏检测的业务要避免这种干扰,map 得工作在独立的zone内,排除这个zone的泄漏检测。所以得重新写了。

实现

业务上主要的4个操作有,插入,删除,查找,遍历
插入,删除,查找都好办,用邻接表实现即可,发生哈希冲突的时候,用链表解决冲突,或者开放定址法,或者链接+红黑树优化。如下

元素x:1, 2, 3, 4, 5, 哈希函数:x % 3,形成的邻接表如下
[0] -> 3
[1] -> 1 -> 4
[2] -> 2 -> 5

负载因子:元素个数 5 除以 桶数量 3 = 1.666,一般是控制在 0.75,超过了要扩容。

  • 重点在于怎么遍历?

假设有 n 个桶,每个桶的元素个数为 f(n)。
简单的方法:先遍历桶,再遍历桶中的链表,那么可以遍历完,复杂度 n * f(n)。

极端的情况,中间有很多空的桶,只有 [0] 上有个 1,[100001] 上有个 2,像这样:
[0] -> 1
[1] -> NULL
...
[1000000] -> NULL
[1000001] -> 2

那么仍然要遍历 1000001 个桶,中间大部分遍历是浪费的。

  • 怎么解决这种浪费?

引入一个链表,因为链表方便插入删除和遍历,不方便随机读取。
一个元素在加入哈希表的时候,也把这个元素的指针加入到这个链表中。
同理,在哈希表中删除的时候,也要通过元素的一个指针,把链表中对应的节点删除。
简单来说就是,哈希表,链表同步增 / 删就行。
那么需要遍历的时候,直接遍历链表就行,其他操作插入和删除还是在哈希表中操作。

那么刚刚的 1000001 次遍历,就可以优化到 2 次。速度加快,同时内存也增加,增加了一个链表所需的内存。空间换时间。

最后速度测试:
6s上,100w次调用,耗时单位 ms,MyHashMap 是手动实现的,其他是系统库的

Name 插入 查找 遍历 清空
MyHashMap 235 18 14 158
unordered_map 1508 60 10 106
map 2276 312 29 120
NSDictionary 486 230 14 11

可以看出 MyHashMap 插入和查找是比较快的,清空较慢。

你可能感兴趣的:(为什么手动实现一个哈希表)