1 概述
在实际开发当中,我们常常使用哈希函数了组织数据,以便能够快速地插入和搜索数据。
通常,我们会用到两种数据结构。
- HashSet: 哈希集合,没有重复的数据
- HashMap: 哈希映射,由键值对组成
2 哈希表中使用的概念
2.1 哈希函数
哈希函数能够将一个大数或字符串映射为一个小整数,这个整数可用作哈希表的索引。
2.2 负荷系数
负荷系数是表中存储的 元素数/桶数
它的主要作用就是权衡时间与空间复杂性。
2.3 容量与初始容量
一般而言,桶的初始容量为 16
。比如 Java 中的 HashMap
。
2.4 阈值
阈值的计算公式是 容量 * 负荷系数
。在 java 中,Hashmap
的初始容量为16,负荷系数为0.75。因此阈值是16 * 0.75 = 12
。所以,向 HashMap
添加第12个元素之后,HashMap
的容量将从16增加到32。
2.5 再散列
如果负荷系数是1.0,当容量消耗完时,会触发再散列。所有的 hashcode
将会被重新计算。
3 哈希表
哈希表使用哈希函数映射到桶中。
3.1 插入新键
- 哈希函数决定分配哪个桶键
- 键值存储在相应的桶中
3.2 查找键值
- 通过哈希函数找到相应的桶
- 在桶里查找键值
如果以y = x % 5
作为哈希函数,那么1987 和 2 将会被放在同一个桶里。
3.3 设计哈希表
哈希函数
理想情况下,key 和 bucket 是一对一的映射。然而,在桶的数量和桶的容量之间总是存在一个权衡。所以,好的哈希函数必须满足下面的要求:
- 计算效率高
- 键值能够均匀分布
减少碰撞或者冲突
冲突意味着两个键值,映射到同一个桶里。
那么,如何处理碰撞呢?
采用链表
如下图所示:
可以看到,使用链表会增加额外的存储。
4 Java 中的 HashSet 和 HashMap
HashSet
里,不存在重复值。下面是它的典型用法:
Set