哈希原理
把节点的键值通过哈希函数计算映射成索引,直接确定在数组中的存储位置。相较于进去搜寻与一个个比较的确定方式,维护的映射确定存储能使得哈希表实现对数据的极速定位来操作(存储、查询、修改、删除)。
一、冲突避免
不同元素的不同键值多个对应映射到哈希表相同索引处时,存储冲突就发生了。为尽量避免哈希表里面去深度存储或降低深度存储时的复杂度,我们要做的就是降低键值存储时的冲突率,通过合理的哈希函数设计或存储的键值多时去扩表调控负载因子。
1. 哈希函数设计
哈希函数要使得要来的键值都能且尽量均匀少重叠地对应转为哈希数组的索引范围内。
1.1 除留余数法
键值 % 数组长度的值域为 0~数组长度 -1,即刚好面向存储的哈希数组所有位置存放。且对于大部分套数据,不规则的模上数组长度得到的也是不规则值域,最后散落在哈希数组中均匀分布的。
1.2 线性函数法
如果套数据连续紧凑,也可以设置成线性函数 A*Key+B 对应连续紧凑地对上数组。
2. 负载因子调控
填入表中的元素越多时,元素之间的存储冲突概率就会变大。大到一定程度时就通过扩表将表的存储冲突率再降下去,动态地调控使表的冲突率维持在一个范围之下。
扩表
扩表时,表的容量改变。如果哈希函数与表的容量相关的,哈希函数也改变,键值映射到的索引也会改变需要更新,即要把所有元素去重新哈希重新映射存储。
扩表代码
private void resize() {
Node[] tmpArr = new Node[array.length * 2];
// 遍历原来的数组 将所有的元素《重新哈希》到新的数组当中
for (int i = 0; i < array.length; i++) {
Node cur = array[i];
while (cur != null) {
// 每个索引桶里面的链表全部节点都要重新哈希
// 记录当前链表节点的下个节点
Node curNext = cur.next;
// 重新映射的索引
int newIndex = cur.key % tmpArr.length;
// 头插
cur.next = tmpArr[newIndex];
tmpArr[newIndex] = cur;
cur = curNext;
}
}
array = tmpArr;
}


