一、什么是哈希表
1.概述
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度这个映射函数叫做散列函数,存放记录的数组叫做散列表。
通俗的理解一下:
- 如果我们有n个元素要存储,那我们就用l个内存单元来存储他们
- 然后我们有一个哈希函数f(x),我们把元素n用函数计算得到哈希值,也就是f(n)
- f(n)就是存储元素n的那个内存单位的位置,也就是元素在l中的下标
2.为什么哈希表查询速度快
理解了哈希表的基本思路,我们也就不难理解为什么哈希表查询效率高了:
由于每个元素都能通过哈希函数直接计算获得地址,所以查找消耗时间非常少。
举个例子:
我们有哈希函数f(n)=n%3,现有元素{1,2,3},我们使用哈希函数分别获得其哈希值,并把哈希值作为下标存入一个数组,
也就是放f(1)=1,f(2)=2,f(3)=0,如果使用传统线性查找,需要遍历四次,而使用哈希函数计算并查找,只需要一步就能找到,
可以看得出,理想情况下,哪怕数列再长,找到某个元素都只需要一步。
3.哈希冲突
按照上文的例子,数列{1,2,3}通过哈希函数f(n)=n%3可以计算出哈希值,但是如果出现两个元素的哈希值相同就会出现哈希冲突,
比如f(1)和f(4)都会算出1,这个时候显然不可能上上面一样通过一个一维数组直接存储。
对此我们有两种方法,即开放地址法和分离链表法:
开放地址法:如果某一哈希值对应的位置已经被占用了,就找另一个没被占用的位置。
- 开放地址法容易产生堆积问题;不适于大规模的数据存储
- 插入时可能会出现多次冲突的现象,而删除时如果元素是多个冲突元素中的一个,需要对后面的元素作处理,实现较复杂
- 结点规模很大时会浪费很多空间
注:关于开放地址法,具体可以参考这篇文章
分离链表法:将散列表的每一个单元都扩展成为一个链表,相同哈希值的元素会被存储在同一个链表中。
- 分离链表法处理冲突简单,且无堆积现象,平均查找长度短
- 链表中的结点是动态申请的
- 相对开放地址法更加节省空间
- 插入与删除结点比较方便
在jdk8中,使用的就是分离链表法,当哈希冲突超过一点的限制,链表会转为红黑树。
二、代码实现
在这里我们实现一个基于分离链表法的哈希表:
1.节点类
1 | /** |
2.单链表
1 | /** |
3.哈希表
1 | /** |