哈希表
通过某种函数使元素的存储位置与它的关键码之间能够成立一种一一映射的关系,那么在查找使通过该函数就可以很快的查找到该元素,而用哈希函数存储元素的数据结构,就叫做哈希表
哈希方法也叫散列方法,哈希函数也叫散列函数,哈希表,也叫散列表
冲突
在多个元素通过哈希函数得到的存储位置相同时,这种现象就叫做哈希冲突或哈希碰撞
常见的哈希函数(降低冲突)
**直接定制法:**取元素的某个线性函数为散列地址:Hash(Key)= A*Key + B 优点:简单、均匀 缺点:需要事先知道元素的分布情况 使用场景:适合查找比较小且连续的情况
除留余数法:
地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将元素转换成哈希地址
平方取中法:如元素为10,这个元素的平方就为100,则取平方中间位置的一个或多个数来做为哈希地址;
平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况
折叠法:把元素的位数分为相等的几部分,如 1024 分为两部分就为10,24,相加的和为34,取4或34(和的后一位或多位)为这个元素的哈希地址
随机数法:取元素的随机数作为哈希地址,如random(key),通常在元素长度不等时采用此法
数学分析法:如:学号 手机号作为元素 就取后4或n位,也就是取这组元素中大多不同的某些位数来作为哈希地址
负载因子调节
负载因子 = 元素个数 / 哈希表长度
而HashMap的负载因子就为0.75
因为负载因子越大时,发生冲突的概率就越大,所以当负载因子大于0.75时,对哈希表进行扩容来解决冲突,而解决冲突的方法一般分为两种:开散列、闭散列
闭散列
闭散列:也叫开放地址法,就是当存放元素发生冲突时,把元素放到下一个空位置去,这样的方法就叫开放地址法,而如何寻找下一个空位置又分为两个方法。
线性探测:按顺序从要放入的位置开始找空位置,然后把元素放进去。但是这样会让冲突元素全部拥堵到一起,
二次线性探测:按照公式去寻找空位置
所以闭散列的空间利用率会比较低,这也是哈希的缺陷
(newHash(key) = (hash(key) + n^2^)%length n为发生第n次冲突 length为哈希表的长度)
平均查找长度ASL = 查找每个元素的查找次数/元素个数
开散列
开散列法又叫链地址法或哈希桶,在哈希表里每个地址里存放链表,每次插入元素时,就转为找到该地址,再把这个元素变为节点存放到这个地址所指向的链表(插入节点到链表时,JDK1.7为头插法,JDK1.8为尾插法)。
HashCode
当传入的类型不是数字,如果放入元素的类型为String、自定义的类,此时就需要通过hashCode方法来找到哈希地址,而这个方法是父类Object的(如果不是同一个对象或地址,则会生成不同的哈希地址),此时可以通过重写hashCode方法来实现相同数据生成相同哈希地址。
hashCode相同,equals不一定相同;但equals相同,hashCode一定相同
哈希桶
当在实例化哈希桶时,如果不给参数,则第一次put时扩充容量至16,在容量满后,再进行2倍扩容
如果给定一个参数 则容量应为2的幂次方,如
参数为16 则容量为16;参数为17,容量为32