开放定址法
这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。这种方法有一个通用的再散列函数形式:
Hi=(H(key)+di)% m i=1,2,…,n
其中H(key)为哈希函数,m 为表长,di称为增量序列。增量序列的取值方式不同,相应的再散列方式也不同。主要有以下三种:
线性探测再散列
dii=1,2,3,…,m-1, 这种方法的特点是:冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。
3,4, 5,6,69,哈希表长度m=11
H(69)=3, 则下一个哈希地址为H1=(3 + 1)% 11 = 4,仍然冲突,再找下一个哈希地址为H2=(3 + 2)% 11 = 5,仍然冲突,再找下一个哈希地址为H2=(3 + 3)% 11 = 6, 此时不再冲突,将69填入6号单元。
二次探测再散列
di=12,-12,22,-22,…,k2,-k2 ( k<=m/2 ),这种方法的特点是:冲突发生时,在表的左右进行跳跃式探测,比较灵活。.
2, 3,4, 5,6,69,哈希表长度m=11
H(69)=3, 则下一个哈希地址为H1=(3 + 1)% 11 = 4,仍然冲突,再找下一个哈希地址为H2=(3 - 1)% 11 = 2,仍然冲突,再找下一个哈希地址为H2=(3 + 4)% 11 = 7, 此时不再冲突,将69填入7号单元。
伪随机探测再散列
di=伪随机数序列。
2, 3,4, 5,6,69,哈希表长度m=11
H(69)=3, 随机数序列为:2,5,9,………,则下一个哈希地址为H1=(3 + 2)% 11 = 5,仍然冲突,再找下一个哈希地址为H2=(3 + 5)% 11 = 8,此时不再冲突,将69填入8号单元。
再哈希法
这种方法是同时构造多个不同的哈希函数:
Hi=RH1(key) i=1,2,…,k
当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。
链地址法(拉链法)
jdk1.8 中HashMap,ConcurrentHashMap都是采用这个方法,使用链表来保存发生hash冲突的key,即不同的key有一样的hash值,将这些发生冲突的 value 组成一个单向链表(只有next指针,没有pre指针)
注意:最坏的就是hash值全都映射在同一个地址上,这样哈希表就会退化成链表。jdk1.8采用红黑树解决这种情况。
建立公共溢出区
这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。
在查找的时候,先与哈希表的相应位置比较,如果查找成功,则返回。否则去公共溢出区按顺序一一查找。在冲突数据少时性能好,冲突数据多的时候耗时。