哈希表
概念
哈希表是一种理想的从顺序表以及平衡树中查找元素的方式,它可以不经过任何比较,一次直接从表中得到想要搜索的元素。如果构造一种存储结构,通过某种函数使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。
- **插入元素:**根据待插入元素的关键码,根据函数计算出存储位置并进行存放
- **搜索元素:**对元素的关键码进行计算,把求得的数据当作元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
该种方法即为哈希 (散列) 方法,哈希方法中使用的转换函数称为哈希 (散列) 函数,构造出来的结构称为哈希表。
例如:数据集合{1,7,6,4,5,9};哈希函数设置为:hash(key) = key % capacity; capacity 为存储元素底层空间总的大小。

上面这种存放元素的方式,不用多次进行关键码的比较,搜索速度比较快。但是,如果集合里再添加一个 14,那么 14 应该放在哪里呢?这里便是要提到冲突。
冲突
对于两个数据元素的关键字 $k_1$ 和 $k_2$ ($i \neq j$),有 $k_1 \neq k_2$,但有:Hash($k_1$) == Hash($k_2$),即:不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。
把具有不同关键码而具有相同哈希地址的数据元素称为'同义词'。
对于冲突,我们要认识到:
由于我们哈希表底层数组的容量往往是小于实际要存储的关键字的数量的,这就导致一个问题,冲突的发生是必然的,但我们能做的应该是尽量的降低冲突率。
引起哈希冲突的一个原因可能是:**哈希函数设计不够合理。**哈希函数设计原则:
- 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有 m 个地址时,其值域必须在 0 到 m-1 之间
- 哈希函数计算出来的地址能均匀分布在整个空间中
- 哈希函数应该比较简单
常见的哈希函数有:
- 直接定制法:取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B。优点是简单、均匀。缺点是需要事先知道关 键字的分布情况 使用场景:适合查找比较小且连续的情况
- 除留余数法:设散列表中允许的地址数为 m,取一个不大于 m,但最接近或者等于 m 的质数 p 作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址
- 负载因子调节,这个下面会重点讲解
其他的方法还有:平方取中法、折叠法、随机数法、数学分析法等,感兴趣的话可以了解一下。
负载因子调节

产生冲突的概率叫做冲突率,已知哈希表中已有的关键字个数是不变的,那么我们能调整的就只有哈希表中的数组的大小。
Java 中负载因子的值为 0.75,即当 填入表中的元素个数 / 散列表的长度 > 0.75 时,产生冲突的概率会很大,这时候我们就要来解决冲突。
冲突的解决
解决哈希冲突两种常见的方法是:闭散列和开散列。
**闭散列:**当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以 把 key 存放到冲突位置中的'下一个'空位置中去。





