一、HashMap的负载因子的作用

当 HashMap 中的元素个数(包含链表、红黑树上的元素)达到数组长度的0.75倍的时候,开始扩容。
 

二、HashMap的负载因子为什么是0.75

主要是为了提高空间利用率和减少查询成本(也可以说是尽可能减少hash冲突)。

三、为什么槽位数必须使用2^n

如果想让 Hash 结果分布更加均匀,首先想到的就是使用取余(%)操作。重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash % length == hash & (length - 1) 的前提是 length 是 2 的 n 次方)。” 并且采用二进制位操作 &,相对于 % 能够提高运算效率,这就解释了 HashMap 的长度为什么是 2 的幂次方。

四、解决Hash冲突的方法

1、开放地址法

公式:fi(key) = (f(key)+di) MOD m (di=0,1,2,3,......,m-1)
key:待放入数组(hash表)的元素;m:数组长度
 
当冲突发生时,使用某种探测技术在散列表中形成一个探测序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探测到开放的地址则表明表中无待查的关键字,即查找失败。

(1)线性探测法

思想是:通过公式计算出元素在数组中的下标,如果下标上没有元素,直接放进去;如果下标中有元素,则公式中的 di 依次 +1 重新计算,直到查找到没有元素的下标。不然数组就满了,需要扩容。

(2)二次探测法

思想是:通过改变 di 的计算方式来查询没有元素的下标,具体计算方式就是 di=-12,12,-22,22,…,-(q * 10 + 2),(q * 10 + 2),q <=m / 2。至于这个 di 的取值我也没研究,摘抄过来的,但是这个探测法的思想得知道。
考虑的情况是,如果通过公式计算出来下标之后的所有下标都有元素占据了,而这个下标的前面的有空闲的,通过第一种方法可以算出来,但是计算的次数比较多,通过这个方法可以减少计算次数。

(3)伪随机数探测再散列

思想是:di 的值是通过随机函数得到的。如果随机函数的种子相同,那么得出来的 di 也相同,查询就ok了。
 
总之,开放定址法只要在散列表未填满时,总是能找到不发生冲突的地址,是我们常用的解决冲突的办法。

2、拉链法

就是当产生 Hash 冲突时,在冲突的节点上形成链表,HashMap 就是使用的拉链法解决的 Hash 冲突。

五、为什么链表长度达到 8 的时候就要转为红黑树了?

当使用 0.75 作为负载因子时,链表中的长度达到 8 几乎是不可能的,均衡策略吧。
引用 HashMap 源码中的注释:
  1. * 0: 0.60653066
  2. * 1: 0.30326533
  3. * 2: 0.07581633
  4. * 3: 0.01263606
  5. * 4: 0.00157952
  6. * 5: 0.00015795
  7. * 6: 0.00001316
  8. * 7: 0.00000094
  9. * 8: 0.00000006
  10. * more: less than 1 in ten million 

六、HashMap扩容时元素的位置发生了什么变化?

分为三种情况:
  • 对于数组上的元素:直接使用已经计算出来的hash值重新计算新下标放入新数组。
  • 对于链表:将一条链表拆分为两条,hash值大于数组长度的新链表放在新数组,小于的就放在原数组。
  • 对于红黑树:将数拆为两条链表,hash值大于数组长度的新链表放在新数组,小于的就放在原数组,最后,重新判断两条链表是否需要转为红黑树。
关键代码:
  1. do {
  2. next = e.next;
  3. if ((e.hash & oldCap) == 0) {
  4. if (loTail == null)
  5. loHead = e;
  6. else
  7. loTail.next = e;
  8. loTail = e;
  9. }
  10. else {
  11. if (hiTail == null)
  12. hiHead = e;
  13. else
  14. hiTail.next = e;
  15. hiTail = e;
  16. }
  17. } while ((e = next) != null);
例如:oldCap 是 16,那么扩容之后的新数组长度就是 32,链表上的元素分别是 7,23,39。(整数的hash值就是本身)
  1. 7 0000 0111
  2. & 160001 0000
  3. ---------------
  4. = 0000 0000 # 0,仍旧在原位
  5.  
  6. 170001 0001
  7. & 160001 0000
  8. ---------------
  9. = 0001 0000 # 非0,需要放在 [17, 32) 之间
  10.  
  11. 230001 0111
  12. & 160001 0000
  13. ---------------
  14. = 0001 0000 # 非0,需要放在 [17, 32) 之间
  15.  
  16. 390010 0111
  17. & 160001 0000
  18. ---------------
  19. = 0000 0000 # 0,仍旧在原位,因为它的的值大于数组的长度

关于HashMap的一些思考的更多相关文章

  1. Java中HashMap扩容机制思考

    1. HashMap在什么条件下扩容 判断HashMap的数组Size大小如果超过loadFactor*capacity,就要扩容. 相关的类属性: capacity:当前数组容量,始终保持 2^n, ...

  2. 面试必问---HashMap原理分析

    一.HashMap的原理 众所周知,HashMap是用来存储Key-Value键值对的一种集合,这个键值对也叫做Entry,而每个Entry都是存储在数组当中,因此这个数组就是HashMap的主干.H ...

  3. 转 HashMap 比较透彻的分析

    HashMap 的实现原理 原文: HashMap 的实现原理 众所周知,HashMap是用来存储Key-Value键值对的一种集合,这个键值对也叫做Entry,而每个Entry都是存储在数组当中,因 ...

  4. 十个问题带你了解和掌握java HashMap

    十个问题带你了解和掌握java HashMap 一.前言 本篇内容是源于 " 由阿里巴巴Java开发规约HashMap条目引发的故事",并在此基础上加了自己的对HashMap更多的 ...

  5. 关于JDK1.7+中HashMap对红黑树场景的思考

    背景 在1.7之前的版本,当数组元素较多(几百.几千,或者更多)的时候,在这种前提扩容,涉及全量元素的遍历和坐标的重新定位,这个耗时会比较长.这是之前存在的一个弊端吧.那么引入红黑树之后就解决了问题, ...

  6. 关于HashMap中hash()函数的思考

    关于HashMap中hash()函数的思考 JDK7中hash函数的实现   static int hash(int h) { h ^= (h >>> 20) ^ (h >&g ...

  7. 关于Java中的HashMap的深浅拷贝的测试与几点思考

    0.前言 工作忙起来后,许久不看算法,竟然DFA敏感词算法都要看好一阵才能理解...真是和三阶魔方还原手法一样,田园将芜,非常可惜啊. 在DFA算法中,第一步是需要理解它的数据结构,在此基础上,涉及到 ...

  8. 【集合框架】JDK1.8源码分析之HashMap(一)

    一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也 ...

  9. Java之HashMap在多线程情况下导致死循环的问题

    PS:不得不说Java编程思想这本书是真心强大.. 学习内容: 1.HashMap<K,V>在多线程的情况下出现的死循环现象   当初学Java的时候只是知道HashMap<K,V& ...

随机推荐

  1. JavaScript 高级程序设计 (第4版) 思维导图/脑图 All In One

    JavaScript 高级程序设计 (第4版) 思维导图/脑图 All In One JavaScript 高级程序设计 (第4版) 思维导图下载 JavaScript 高级程序设计 (第4版) 脑图 ...

  2. 如何在手机上实现 H5 页面全屏显示

    如何在手机上实现 H5 页面全屏显示 fullscreen 隐藏头部地址栏 隐藏底部导航栏 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才 ...

  3. 使用 js 实现十大排序算法: 选择排序

    使用 js 实现十大排序算法: 选择排序 选择排序 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

  4. 2016 JS 笔试题汇总:

    1 1 1 CS&S(中软国际): 1 JavaScript 循环表达式: 2  JavaScript表达式boolean返回值: 3 网页中的事件/HTML 事件属性/JavaScript ...

  5. very useful English Acronyms in Programming for Programmer

    very useful English Acronyms in Programming for Programmer alias / shorthand / acronyms 别名 / 简写 / 缩略 ...

  6. website text select notes menu

    website text select notes menu website 文字选择笔记菜单(下划线, 标记, 复制, 分享) 下划线, 标记 https://time.geekbang.org/ ...

  7. search cascade select & AntD

    search cascade select & AntD Antd https://ant.design/components/cascader-cn/#components-cascader ...

  8. js字典

    传送门https://www.2cto.com/kf/201709/680989.html 点击

  9. 黑马来袭!NGK生态所二月上线!

    日前,加密货币交易所Coinbase Global Inc,向美国证券交易委员会申请首次公开募股,成为首家公开上市的加密货币交易所."Coinbase上市,给行业带来更多的是信心.让大家看到 ...

  10. 07.k近邻算法kNN

    1.将数据分为测试数据和预测数据 2.数据分为data和target,data是矩阵,target是向量 3.将每条data(向量)绘制在坐标系中,就得到了一系列的点 4.根据每条data的targe ...