深入理解HashMap

413 阅读4分钟

背景

受到疫情的影响,大环境的经济状况不好,各大公司都采取了措施,许多技术强、经验丰富的大佬也被无情的裁掉了;感觉自己应该要紧张起来,作为技术人,提升自身的技术水平是首要任务,打铁还需自身硬。最近多学习,多看书,并把自己得到的知识分享到博客上,算是对自己的督促。争取一周更新两个技术点,工作日一次,周末一次。正所谓不积跬步无以至千里,不积小流无以成江海;谁都不是天生就知道那么多的,都是从低层次,一步一步攀的高峰的;加油吧,少年!!!

前言

上一章说到的是位的运算,通过查阅HashMap相关资料,发现HashMap的源码中就用到了,真是现学现用,这些计算机基础的知识点,我以后还会做学习分享,给自己加油打气!!

附上链接: java的位运算

UML关系图

HashMap相关

解释:HashMap继承AbstractMap抽象类,实现了Map接口、Cloneable接口、Serializable接口;AbstractMap抽象类实现了Map接口。

Map家族全家照

首先我们了解什么是HashMap

HashMap是Map接口的实现,是常用的Map实现

HashMap特点

  1. HashMap查询快,插入较慢;
  2. 查询快,理想状态下,时间复杂度是O(1)当然如果链表较长,或者需要遍历红黑树,时间复杂度要高一些
  3. 插入慢,根据不同场景可能是O(1)无冲突,直接插入到数组中、O(n)有冲突,插入到链表中、O(logn)有冲突,插入到红黑树中
  4. 线程不安全,高并发场景下,会出现问题
  5. 允许键值为NULL

HashMap涉及到的概念

  1. Hash算法(哈希、散列算法),是一种摘要算法;可以将任意长度的输入转换为固定长度的输出,是一种压缩映射。HashMap使用Hash主要是用来确定,键值对存放在桶中的位置。
  2. 数组,基本的数据结构;是顺序存放元素的集合,数组需要在内存中分配一块空间。HashMap的桶的概念就是数组,数组存放Hash不冲突的键值在HashMap中键值就是Node<K,V>
  3. 链表,基本数据结构;通过链表每个元素的指针来保证顺序性,在内存上是非连续、非顺序的。当HashMap中的Key出现Hash冲突时,就会在冲突的位置下,创建出链表来;当然如果链表长度超过8,就会树化
  4. 红黑树,平衡二叉树进化版;HashMap使用红黑树是因为当链表比较长的时候,查询复杂度会直线上升O(n);使用红黑树可以将查询、插入、删除的时间复杂度降到 O(logn),提高效率;如果红黑树的数量减低到了6,就会转回链表
  5. 负载因子,是指当桶内元素数量超过桶长 * 负载因子,就会进行扩容;负载因子是根据泊松分布得出;负载因子越大,空间利用率就越高,但随之而来的是,Hash冲突的概率也大,链表或树的长度也就越大,性能降低;负载因子越小,Hash冲突越小,相对性能越高,空间利用率低,且经常扩容也会影响到性能。
  6. 扩容,根据5的条件,进行扩容;原来长度是16,那么会创建出新的数组,数组长度是32,然后对之前的Node进行Hash重新计算位置,放入新的数组中;所以说扩容时,会影响到性能

思考

为什么桶的默认值是1<<4 //aka 16

首先创建HashMap时,如果不指定长度,默认是16;如果要自己指定长度,建议是2的次幂;

说明:&与运算,同位的两个值都为1,结果是1;其他的都是0

  1. 桶index的算法:hash(key) & (lenth - 1),hash值 与运算 长度 -1
  2. 如果长度不是2的次幂,得出的二进制最后一位必然是1,然后减一,最后的二进制是0;0和任意数做与运算都是0,这意味着,这一位失去确定位置的作用,从而Hash冲突的概率提升;从而降低了操作的性能。