HashMap原理入门

兴趣 = 未知 + 好奇

一. HashMap是啥

一个数据容器,每组数据包含一个键(Key),和一个值(Value),故称 “K-V键值对”

HashMap要求容器内的 Key 唯一,原因是HashMap需要根据 Key 来区分不同的数据,而对 Value 无要求

所以可认为,Key 是 HashMap 中的唯一标识,而 Value 就是 Key 的附带产品( Value 被绑定到 Key上 )

因此,管理HashMap的关键,就是管理 Key

二. HashMap本质

HashMap主要是通过数组保存元素,HashMap 针对 Key 经过一系列的计算,就会得出某 Key 应该保存到数组的哪个位置上

这个数组美其名曰:hash表
计算方法统称为:hash算法

HashMap,音译 + 意译 = “哈希映射” —— 再次证明了它的本质就是 hash

三. hash

hash算法 + 数组 = hash表

1. hash算法

Java中,每个对象都有一个 hashCode() 方法,它会返回一个 6 位的 16 进制的 int 数字给该对象编号

HashMap第一步就是调用 Key.hashCode(),得到最原始的 hash 值

在这里插入图片描述

接下来拿这个 hash 值一顿位运算,目的是为了让 hash 值的二进制的0、1在各个位上分布均匀,以此减少对象hash值冲突的概率,这样就得到了最终的 hash 值

2. 数组

HashMap中的数组不是一般类型的数组,因为HashMap保存的是K-V键值对,即把一个Key和一个Value绑定到一起,作为一个整体保存到数组的某个位置上,所以数组的类型需要自定义类,自定义类中至少有两个属性,分别保存Key和Value

在这里插入图片描述

3. hash表

由最终 hash 值,跟数组发生某种关系后,就能确定每个 K-V 在数组中的位置

通过hash算法确定元素在数组中的位置,这样的数据结构就是hash表

在这里插入图片描述

四. hash冲突

前面说过,hashCode() 方法会返回一个6位的16进制数,可是这个数最多只能标明
15×(165 + 164 + 163 + 162 + 161 + 160) + 1 = 1677 7216 种不同的对象

如果内存足够大,或者hash算法足够垃圾,hash冲突会不可避免

hash冲突带来的问题就是,原本一个人蹲一个坑的厕所,现在同一个坑硬要再蹲一个人,两人势必会竞争资源,两残惧伤

为了解决hash冲突的问题,HashMap采用了链地址法

当某坑位有超过一人时,让后来的排队

在这里插入图片描述
这样形成一个链表结构,当需要找寻某Key时,先用该Key的hash值定位到坑位,然后再遍历队伍,比较hash值

五. 扩容

HashMap之所以应用广泛,一方面得益于它本身的K-V存储特点,另一方面受益于hash带来的增删元素很迅速,理想情况下,用非人类的话讲,叫时间复杂度为O(1)

但是理想终归理想,现实依然会给理想上课

假设你非常幸运,创建了一个HashMap,它里面有一些元素是这样存储的

在这里插入图片描述

像这种比中一次5块钱彩票概率还低的事件,带来的问题是失去了hash表的作用,还不如直接用链表保存

发生这种情况的原因之一是hash表坑位有限,即便采用了很好的hash算法,还是会有hash冲突

直观的看,当链表太长时就需要扩容了,以降低遍历链表的时间

HashMap每增加一个K-V后都会做判断,当总的K-V数超过hash表容量的0.75倍后,每次倍增扩容

在这里插入图片描述

六. 结构转化

当hash表容量超过64且单坑位链表节点数超过6时,将链表转为树结构

这个操作也是为了增加遍历单坑位链表的速度

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44676331/article/details/113788507