Q0:HashMap是如何定位下标的?
A:先获取Key,然后对Key进行hash,获取一个hash值,然后用hash值对HashMap的容量进行取余(实际上不是真的取余,而是使用按位与操作,原因参考Q6),最后得到下标。

Q1:HashMap由什么组成?
A:数组+单链表,jdk1.8以后又加了红黑树,当链表节点个数超过8个(m默认值)以后,开始使用红黑树,使用红黑树一个综合取优的选择,相对于其他数据结构,红黑树的查询和插入效率都比较高。而当红黑树的节点个数小于6个(默认值)以后,又开始使用链表。这两个阈值为什么不相同呢?主要是为了防止出现节点个数频繁在一个相同的数值来回切换,举个极端例子,现在单链表的节点个数是9,开始变成红黑树,然后红黑树节点个数又变成8,就又得变成单链表,然后节点个数又变成9,就又得变成红黑树,这样的情况消耗严重浪费,因此干脆错开两个阈值的大小,使得变成红黑树后“不那么容易”就需要变回单链表,同样,使得变成单链表后,“不那么容易”就需要变回红黑树。

Q2:Java的HashMap为什么不用取余的方式存储数据?
A:实际上HashMap的indexFor方法用的是跟HashMap的容量-1做按位与操作,而不是%求余。(这里有个硬性要求,容量必须是2的指数倍,原因参考Q6)

Q3:HashMap往链表里插入节点的方式?
A:jdk1.7以前是头插法,jdk1.8以后是尾插法,因为引入红黑树之后,就需要判断单链表的节点个数(超过8个后要转换成红黑树),所以干脆使用尾插法,正好遍历单链表,读取节点个数。也正是因为尾插法,使得HashMap在插入节点时,可以判断是否有重复节点。

Q4:HashMap默认容量和负载因子的大小是多少?
A:jdk1.7以前默认容量是16,负载因子是0.75。

Q5:HashMap初始化时,如果指定容量大小为10,那么实际大小是多少?
A:16,因为HashMap的初始化函数中规定容量大小要是2的指数倍,即2,4,8,16,所以当指定容量为10时,实际容量为16。

☆☆☆☆Q6:容量大小为什么要取2的指数倍?
A:两个原因:1,提升计算效率:因为2的指数倍的二进制都是只有一个1,而2的指数倍-1的二进制就都是左全0右全1。那么跟(2^n - 1)做按位与运算的话,得到的值就一定在【0,(2^n - 1)】区间内,这样的数就刚合适可以用来作为哈希表的容量大小,因为往哈希表里插入数据,就是要对其容量大小取余,从而得到下标。所以用2^n做为容量大小的话,就可以用按位与操作替代取余操作,提升计算效率。2.便于动态扩容后的重新计算哈希位置时能均匀分布元素:因为动态扩容仍然是按照2的指数倍,所以按位与操作的值的变化就是二进制高位+1,比如16扩容到32,二进制变化就是从0000 1111(即15)到0001 1111(即31),那么这种变化就会使得需要扩容的元素的哈希值重新按位与操作之后所得的下标值要么不变,要么+16(即挪动扩容后容量的一半的位置),这样就能使得原本在同一个链表上的元素均匀(相隔扩容后的容量的一半)分布到新的哈希表中。(注意:原因2(也可以理解成优点2),在jdk1.8之后才被发现并使用)

Q7:HashMap满足扩容条件的大小(即扩容阈值)怎么计算?
A:扩容阈值=min(容量*负载因子,MAXIMUM_CAPACITY+1),MAXIMUM_CAPACITY非常大,所以一般都是取(容量*负载因子)

Q8:HashMap是否支持元素为null?
A:支持。

☆☆☆Q9:HashMap的 hash(Obeject k)方法中为什么在调用 k.hashCode()方法获得hash值后,为什么不直接对这个hash进行取余,而是还要将hash值进行右移和异或运算?
A:如果HashMap容量比较小而hash值比较大的时候,哈希冲突就容易变多。基于HashMap的indexFor底层设计,假设容量为16,那么就要对二进制0000 1111(即15)进行按位与操作,那么hash值的二进制的高28位无论是多少,都没意义,因为都会被0&,变成0。所以哈希冲突容易变多。那么hash(Obeject k)方法中在调用 k.hashCode()方法获得hash值后,进行的一步运算:h^=(h>>>20)^(h>>>12);有什么用呢?首先,h>>>20和h>>>12是将h的二进制中高位右移变成低位。其次异或运算是利用了特性:同0异1原则,尽可能的使得h>>>20和h>>>12在将来做取余(按位与操作方式)时都参与到运算中去。综上,简单来说,通过h^=(h>>>20)^(h>>>12);运算,可以使k.hashCode()方法获得的hash值的二进制中高位尽可能多地参与按位与操作,从而减少哈希冲突。

Q10:哈希值相同,对象一定相同吗?对象相同,哈希值一定相同吗?
A:不一定。一定。

Q11:HashMap的扩容与插入元素的顺序关系?
A:jdk1.7以前是先扩容再插入,jdk1.8以后是先插入再扩容。

Q12:HashMap扩容的原因?
A:提升HashMap的get、put等方法的效率,因为如果不扩容,链表就会越来越长,导致插入和查询效率都会变低。

Q13:jdk1.8引入红黑树后,如果单链表节点个数超过8个,是否一定会树化?
A:不一定,它会先去判断是否需要扩容(即判断当前节点个数是否大于扩容的阈值),如果满足扩容条件,直接扩容,不会树化,因为扩容不仅能增加容量,还能缩短单链表的节点数,一举两得。

我的总结
hashMap.put(key,value)---entry对象---根据计算hash值---按位与----得到下标
jdk1.7
put ---判断数组是否为空,如果为空进行初始化,初始化的是数组容量(@Q6:必须为2的幂次方)
---判断key是否为空,执行方法,key为null存在index为0的位置
---根据key得到hash,对key进行hashcode,(@Q9进行右移和异或运算)
---根据hash值和容量得到下标,indexFor方法(@Q6hash值s与容量-1进行按位与操作)
---覆盖逻辑,遍历链表,短路与先判断hash值是否相等,再判断key,value覆盖,返回oldvalue
---addEntry(hash,key,value,i),先有(@Q6扩容机制),再根据四个值,头插法或尾插法插入
get ---判断key是否为空
---根据key获得entry,计算hash值,得到下标,遍历链表,先比较hash再比较key,就得到了
---根据entry获得value
jdk1.8
1---判断数组是否为空,调用resize方法进行初始化(resize方法李有初始化逻辑和扩容逻辑)
1---判断数组元素是否为空(计算出下标得到的这个数组元素,其实就是头节点)
2---为空则添加新节点
1---数组元素不为空意味着存在一个或一个以上元素,则为链表或红黑树
2---判断hash值和key,短路与操作,是同一个节点则继续往下执行2,不是则执行判断3
3---判断是否为红黑树
4---是树,则添加新节点
4---不是树则为链表,遍历链表,判断是否为尾节点
5---是尾节点则添加新节点
4---判断hash和key,返回e执行下一步的2操作覆盖操作
2---同一个节点则执行覆盖操作,同jdk1.7
结果不是了添加新节点,就是因为相同所以覆盖原来的旧节点
jdk1.8先加数据再扩容,如果扩容可以解决链表太长问题就不变红黑树

1.7和1.8差不多流程都是
1.判断数组是否为空
2.遍历链表(1.8就是多加了遍历红黑树)
3.判断hash和key,相同则覆盖,不同就是加新元素

深入探讨HashMap的底层结构、原理、扩容机制相关推荐

  1. 阿里P8架构师谈:深入探讨HashMap的底层结构、原理、扩容机制

    摘要 HashMap是Java程序员使用频率最高的用于映射(键值对)处理的数据类型. 随着JDK(Java Developmet Kit)版本的更新,JDK1.8对HashMap底层的实现进行了优化, ...

  2. 聊聊Java系列-集合之HashMap底层结构原理

    前言           由于HashMap在我们的工作和面试中会经常遇到,所以搞懂HashMap的底层结构原理就显得十分有必要了.在JDK1.8之前,HashMap的底层采用的数据结构是数组+链表, ...

  3. java集合:HashMap的底层实现原理

    HashMap的底层实现原理是面试中出现频率非常高的一道面试题,本文将对HashMap的底层实现原理做一个简要的概况和总结,便于复习. 一.对于Map集合存储结构的理解 首先介绍以HashMap为典型 ...

  4. Java HashMap的底层实现原理

    一.Java HashMap的底层实现原理(以jdk7为例) 1.HashMap map = new HashMap(); 在实例化以后,才在底层创建了一个长度为16的一维数组 Entry [] ta ...

  5. HashMap的底层结构、原理、扩容机制

    文章目录 一.问题 Q0:HashMap是如何定位下标的? Q1:HashMap由什么组成? Q2:Java的HashMap为什么不用取余的方式存储数据? Q3:HashMap往链表里插入节点的方式? ...

  6. 复习一波,hashMap的底层实现原理

    前言 HashMa是Java中最常用的集合类框架,也是Java语言中非常典型的数据结构,同时也是我们需要掌握的数据结构: java中集合的分类: java中的集合可以分为:单列集合(collectio ...

  7. 面试题:请说一下HashMap的底层实现原理。

    个人网站:riun's blog (现在都在这上面写东西) HashMap使用数组加链表实现.每个数组元素中储存着链表. 当使用put方法储存key-value键值对时,会先调用key的hashCod ...

  8. V7000存储底层结构原理+V7000存储数据恢复案例

    Storwize V7000(也就是我们常说的V7000)是新推出的一款中端存储系统,这款系统的定位虽然在中端,但是Storwize V7000提供有存储管理功能,这一功能以前只有高端存储才拥有(例如 ...

  9. 面试题:HashMap的底层运行原理(通俗易懂)

    一图带你读懂HashMap的底层运行:

最新文章

  1. [PHP]常量的一些特性
  2. Elasticsearch java api(五) Bulk批量索引
  3. Struct2_定义拦截器并使用注解方式作用在Action的方法中
  4. 用Python开始机器学习(4:KNN分类算法)
  5. Epoll详解及源码分析
  6. 表单PostGet两个长度限制问题的分析
  7. Ruby对象、变量和常量
  8. 14岁AI天才的钢铁之心
  9. 巴什博弈:取石子游戏
  10. linux 查看网络流量
  11. AndroidManifest.xml详解(service)
  12. C++ vector 类学习笔记(转)
  13. 51单片机毕业设计题目大全
  14. C语言图形库——easyx的使用
  15. python二维随机游走_Python模拟随机游走图形效果示例
  16. python word.documents.open报错_Python教程:[43]Word基本操作
  17. Linux c在图片添加时间水印,如何在照片上添加时间水印
  18. 高级篇Docker复杂安装详说
  19. JS判断用户输入是否为素数
  20. RANSAC如何动态更新最大迭代次数

热门文章

  1. iOS - Mac 锁屏快捷键设置
  2. 为撒电大计算机专业学籍出来的慢,2021年网络教育学籍什么情况下会被取消了...
  3. HTML期末学生大作业-资讯网站html+css+javascript
  4. 线性代数——矩阵的初等变换
  5. 微信分享链接制作(先行版)
  6. “乙”的五笔编码为什么是nnl
  7. 主要的排序算法——Java实现
  8. pr基本技巧及相关教程
  9. 【MAC使用技巧】浏览器设置F5刷新快捷键
  10. linux课程设计QQ聊天带界面,仿QQ聊天系统课程设计解析.doc