数据结构与算法-哈希表

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/zhangdong2012/article/details/99707918

数组和链表的特点

数组支持随机存取;
分配数组需要连续的内存空间;
链表不支持随机存取;
链表不需要连续的内存空间;

哈希表

由于数组和链表具有各自的优缺点和适用场景,所以为了进一步提高数据的读写性能,哈希表就诞生了,即哈希表就是”数组+链表“,数组构成hash bucket,链表解决hash冲突。然而这里所说的链表已经不是狭义上的链表了,它可以是任何适用的数据结构,例如AVL树,红黑树等。在java 8中的HashMap,当链表中的数据超过一定的阈值后,链表就会转化为红黑树,以避免因为hash冲突过于严重,导致HashMap性能急剧下降。

哈希函数

哈希函数是哈希表中最重要的函数,它决定了一个元素在哈希表中所属bucket的位置,如果哈希函数设计的不合理,那么就会产生比较严重的哈希冲突,使得哈希表性能下降。

在不同的应用领域,哈希函数的设计原则可能有所差异,但有一个总的指导思想:哈希函数在计算哈希值时,应尽量包含元素的更多信息,什么意思呢?

public class Person {
       private String name;
       private Integer age;
       @Override
       public int hashCode() {
              return name.hashCode() + age.hashCode();
       }
}

如果把Person对象当做一个哈希中的元素时,Person的hashCode()首先会被调用,如果这个hashCode()包含了Person完整的信息,就能尽可能的避免出现hash冲突。

用Effective Java中的话说:

为截然不同的两个对象(equal返回false)产生截然不同的hash code,有助于提高散列表的性能。即可在一定程度上有效的避免哈希冲突。

另外,哈希函数的设计也不能过于复杂,由于对于哈希表的绝大多数操作都需要调用哈希函数,如果设计的过于复杂,那么势必影响散列表的性能。

总结一下一个号的哈希函数具有的特点:

  • 均匀分布
  • 逻辑简单

解决哈希冲突

链地址法

链地址法是最常用的解决hash冲突方式,即在一个hash bucket下挂载一个链表,将所有hash值相同的元素放到这个链表下。

但这种方式有一个弊端,在极端情况下,如果所有的元素都产生可hash冲突,被放到了同一个bucket下,那么哈希表就退化成了一个链表,相关操作的时间复杂度从O(1)退化成了O(n)。

为了解决这个问题,java中的HashMap 当链表中的节点个数达到8个时,链表就会升级为红黑树,进而保证HashMap不会因为hash冲突而性能急剧下降。

哈希碰撞攻击属于Dos攻击的一种,它的主要原理就是利用实现设计好的数据,使哈希表产生严重的哈希冲突,哈希表性能急剧下降,进而拖垮系统。它主要就是利用了哈希表的存储结构的特点。

开放地址法

在链地址法中,每个元素它所对应的hash bucket都是固定的,但是开放地址法则采用了完全不同的思路。当产生hash冲突时,就会试图向下寻找不为空的hash bucket,并将其放在其中。

这种方法应用貌似不是很多,本人也不甚了解。

猜你喜欢

转载自blog.csdn.net/zhangdong2012/article/details/99707918