哈希表理解

哈希表理解

  • 什么是哈希表?
    • 哈希表怎么存储数据的?
    • 哈希冲突
    • 处理哈希冲突
    • 哈希表的扩容
    • 哈希表怎么读取数据

什么是哈希表?

百度百科:

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash)
函数。

根据百度百科解释,哈希表是根据key来直接获取数据的数据结构,查询速度很快。哈希表本质来说就是数组
哈希表简单实现可以有两种方式:
1、数组+链表
2、数组+二叉树
无论哪个都必须要有数组,链表和二叉树都是解决基于数组上的哈希冲突问题,本质上是哈希冲突的一种解决方法。
举个通俗例子:
根据某人的名称在一个按照人名首字母排序的电话列表张查询某人的号码,即人名(关键字key),按照人名首字母(散列函数f(x)),查到某人的电话号码(值value)。
哈希表理解_第1张图片
拿数组+链表举例,如上的abcd…对应数组的索引中,如果只有张一,这里的z对应的数组索引存数据的entry(键值对key-value:key=张一,value=1231***)
键值对:就是一个值对应另外一个值,这里就是张一对应张一的手机号1231***,张一就是Key,张一的手机号131***就是value,哈希表存放的就是这样的键值对。所以在哈希表中,a映射到b,a就叫做键值,b就叫做a的哈希值,即hash值。

哈希表怎么存储数据的?

现在知道了哈希表的本质是个数组,根据上图,现在有个长度为8的数组,假如现在要将张一的信息。存入哈希表中,也就是这个数组中,考虑怎么存放呢?
这里有个姓名张一,将张一作为key,将key通过哈希函数计算得到一个值7,这个值就是用来确定这个entry(即张一信息,key=张一;value=1231***)要存放在哈希表中的位置的,实际上这个7就是数组的一个下标值。

哈希冲突

但是这里有个问题,再来张二,张三开头仍旧是z的怎么办呢?这就是hash冲突,张二张三通过哈希函数计算同样计算获得的也是7。那么后面来的数据怎么存放呢?

处理哈希冲突

解决哈希冲突的办法有好几种。

  1. 开放寻址法:Hi=(H(key) + di) MOD m,i=1,2,…,k(k<=m-1),其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法:
    1.1. di=1,2,3,…,m-1,称线性探测再散列;
    1.2. di=12,-12,22,-22,⑶2,…,±(k)2,(k<=m/2)称二次探测再散列;
    1.3. di=伪随机数序列,称伪随机探测再散列。
  2. 再散列法:Hi=RHi(key),i=1,2,…,k RHi均是不同的散列函数,即在同义词产生地址冲突时计算另一个散列函数地址,直到冲突不再发生,这种方法不易产生“聚集”,但增加了计算时间。
  3. 链地址法(拉链法)
  4. 建立一个公共溢出区

开发寻址法:既然位置被占用了,再找位置就行了。有许多实现,最基本的就是当前位置被占,往后检查当前位置的下一个位置是否被用,没被占用就直接用,占用了就继续往下找知道找到。因为有扩容机制,所以不存在找不到的情况。
拉链法(链表法):entry中除了存储key,value外,还存储指向下一个数据的next指针。比如查询张三,当前数组中7位置存储的entry是张一,经过判断key不是张三,他就拿entry的next指针到数组外的另一个位置找到张二的entry,再比较也不是,再根据张二的entry中存储的指针找到张三,比较key匹配上了这才返回。张一张二张三的entry中next间关联形成了一个链表。
哈希表理解_第2张图片

哈希表的扩容

扩容一个是没有空位置放数据了,还有一个是当哈希表被占的位置比较多的时候,哈希冲突出现的概率也会变高,所以有必要扩容。
有一个增因子概念,也叫做负载因子,当哈希表容量到达负载因子的值是就会触发扩容。拿HashMap来说,假如总容量是100,负载因子是0.75,当所占位置达到75个时。就会触发扩容。这个扩容也不是简单的把数组扩大,而是新创建一个数组是原来的2倍,然后把原数组的所有Entry都重新Hash一遍放到新的数组。
这里的重新Hash一遍是因为数组扩大了,所以一般哈希函数也会有变化,这里的Hash也就是把之前的数据通过新的哈希函数计算出新的位置来存放。

哈希表怎么读取数据

如要通过张三查询张三的手机号,先根据张三通过哈希函数算出位置7,然后根据位置7拿数据,拿到entry看key是不是张三,不是再根据entry的next去找下一个,再比较,以此类推。

如果冲突较多,会导致链表过深,导致查询慢,所以jdk1.8在链表过长后会升级为为红黑树存储,当数据变少后,又会退化为链表。

你可能感兴趣的:(java基础,哈希表)