**这里就涉及到了一个链表中数据存储时,进行“树化”和“链化”的一个过程,**那么什么是“树化”和“链化”呢?

当我们在对键值对进行存储的时候,如果我们在同一个数组下标下存储的数据过多的话,就会造成我们的链表长度过长,导致进行删除和插入操作比较麻烦,所以在java中规定,**当链表长度大于8时,我们会对链表进行“树化”操作,****将其转换成一颗红黑树(一种二叉树,左边节点的值小于根节点,右边节点的值大于根节点),**这样我们在对元素进行查找时,就类似于进行二分查找了,这样的查找效率就会大大增加。

但是当我们进行删除操作,将其中的某些节点删除了之后,链表的长度不再大于8了,这个时候怎么办?难道就要赶紧将红黑树转化为链表的形式吗?其实并不是,只有当链表的长度小于6的时候,我们才会将红黑树重新转化为链表,这个过程就叫做“链化”。

过程图示如下:

那么为什么要在长度8的时候进行“树化”,而在长度小于6的时候才进行“链化”呢?为什么不直接在长度小于8的时候就进行“链化”?

**主要原因是因为:**当删除一个元素,链表长度小于8的时候直接进行“链化”,而再增加一个元素,长度又等于8的时候,又要进行“树化”,这样反复的进行“链化”和“树化”操作特别的消耗时间,而且也比较麻烦。所以程序就规定,只有当当链表长度大于等于8的时候才进行“树化”,而长度小于6的时候才进行“链化”,其中关于8树化、6链化这两个阈值希望大家牢记!

4、链表中是按照怎样的顺序存放数据的?

=======================

我们现在已经知道了HashMap中的元素是如何存放的,但是有时候面试官可能还会问我们,在HashMap中,向链表中存储元素是在头结点存储的还是在尾节点存储的?

这个我们需要知道,对于HashMap中链表元素的存储。

在JDK1.7以及前是在头结点插入的,在JDK1.8之后是在尾节点插入的。

5、Hash(key)方法是如何实现的?

========================

我们现在已经知道了HashMap中的元素是如何存储的了,那么现在就是如何应该根据key值进行相应的数组下标的计算呢?

我们知道HashMap的初始容量是16位,那么对于初始的16个数据位,如果将数据按照key的值进行计算存储,一般最简单的方法就是根据key值获取到一个int值,方法是:

int hashCode = key.hashCode()

然后对获取到的hashCode与16进行取余运算,

hashCode % 16 = 0~15

这样得到的永远都是0—15的下标。这也是最最原始的计算hash(key)的方法。

**但是在实际情况下,这种方法计算的hash(key)并不是最优,**存放到数组中的元素并不是最分散的,而且在计算机中进行余运算其实是非常不方便的、

**所以为了计算结果尽可能的离散,现在计算数组下标最常用的方法是:**先根据key的值计算到一个hashCode,将hashCode的高16位二进制和低16位二进制进行异或运算,得到的结果再与当前数组长度减一进行与运算。最终得到一个数组下标,过程如下:

int hashCode = key.hashCode()

int hash = hash(key) = key.hashCode()的高16位^低16位&(n-1)  其中n是当前数组长度

同时在这里要提醒一点。

在JDK1.7和JDK1.8的时候对hash(key)的计算是略有不同的

JDK1.8时,计算hash(key)进行了两次扰动

JDK1.7时,计算hash(key)进行了九次扰动,分别是四次位运算和五次异或运算

其中扰动可能理解为运算次数

以上就是Hash(key)方法的实现过程。

6、为什么HashMap的容量一直是2的倍数?

===========================

HashMap的容量之所以一直是2的倍数,其实是与上面所说的hash(key)算法有关的,

原因是只有参与hash(key)的算法的(n-1)的值尽可能都是1的时候,得到的值才是离散的。假如我们当前的数组长度是16,二进制表示是10000,n-1之后是01111,使得n-1的值尽可能都是1,对于其他是2的倍数的值减1之后得到值也是这样的。

所以只有当数组的容量长度是2的倍数的时候,计算得到的hash(key)的值才有可能是相对离散的,

7、Hash冲突如何解决?

=================

什么是Hash冲突?就是当我计算到某一个数组下标的时候,该下标上已经存放元素了,这就叫Hash冲突,很显然,如果我们计算数组下标的算法不够优秀的时候,很容易将存储的数据积累到同一个下标上面,造成过多的Hash冲突。

那么如何解决hash冲突?

最应该解决的其实就是让存储的key计算得到的数组下标尽可能的离散,也就是要求hash(key)尽可能的优化,数组长度是2的倍数。这也就是Hash冲突的主要解决方法。

具体可以查看下面HashMap关键部分的底层源码:

Hash(key)的底层实现

/*** Applies a supplemental hash function to a given hashCode, which* defends against poor quality hash functions.  This is critical* because HashMap uses power-of-two length hash tables, that* otherwise encounter collisions for hashCodes that do not differ* in lower bits. Note: Null keys always map to hash 0, thus index 0.*/static int hash(int h) {// This function ensures that hashCodes that differ only by// constant multiples at each bit position have a bounded// number of collisions (approximately 8 at default load factor).h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);}

put(key,value)方法的底层实现

/*** Associates the specified value with the specified key in this map.* If the map previously contained a mapping for the key, the old* value is replaced.** @param key key with which the specified value is to be associated* @param value value to be associated with the specified key* @return the previous value associated with <tt>key</tt>, or*         <tt>null</tt> if there was no mapping for <tt>key</tt>.*         (A <tt>null</tt> return can also indicate that the map*         previously associated <tt>null</tt> with <tt>key</tt>.)*/public V put(K key, V value) {if (key == null)return putForNullKey(value);int hash = hash(key.hashCode());int i = indexFor(hash, table.length);for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;}

8、HashMap是如何扩容的?

====================

我们在上面说到了HashMap的数组的初始容量是16,但是很显然16个存储位是显然不够的,那么HashMap应该如何扩容呢?

在这里需要用到一个参数叫“扩容因子”,在HashMap中“扩容因子”的大小是0.75,

**我们上面也提到过,对于初始长度为16的数组,当其中存储的数据长度等于16*0.75=12时。就会对数组元素进行扩容,扩容量是原来数组容量的2倍,**也就是当前是15话,再扩容就是扩容32个数据位。

9、扩容后元素怎么存放的?

=================

我们知道HashMap的数组在进行扩容之后,数组长度是增加的,那么这个时候,后面新扩容的部分就是空的。但是这个时候我们就应该让后面的数据位空着吗?显然是不可能的,这样会造成内存的很大浪费。

因此在HashMap的数组扩容之后,原先HashMap数组中存放的数据元素会进行重新的位置分配,重新将元素在新数组中进行存储。以充分利用数组空间。

10、JDK1.7和JDK1.8对HashMap的实现比较

=================================

在JDK1.7和JDK1.8中对HashMap的实现是略有不同的,最后我们根据上面的讲解对JDK1.7和JDK1.8在HashMap的实现中的不同进行分析比较。

(1)、底层数据结构不同

在HashMap的put过程中,JDK1.7时是没有红黑树这一概念的,直接是进行的链表存储,在JDK1.8之后才引入了红黑树的概念,来优化存储和查找。

(2)、链表的插入方式不同

在HashMap向链表中插入元素的过程中,JDK1.7时是在表头节点插入的,JDK1.8之后是在尾节点插入的。

1200页Java架构面试专题及答案

小编整理不易,对这份1200页Java架构面试专题及答案感兴趣劳烦帮忙转发/点赞一下,然后点击这里即可免费领取!

百度、字节、美团等大厂常见面试题

。**

(2)、链表的插入方式不同

在HashMap向链表中插入元素的过程中,JDK1.7时是在表头节点插入的,JDK1.8之后是在尾节点插入的。

1200页Java架构面试专题及答案

小编整理不易,对这份1200页Java架构面试专题及答案感兴趣劳烦帮忙转发/点赞一下,然后点击这里即可免费领取!

[外链图片转存中…(img-h0nX4MWG-1628506887296)]

[外链图片转存中…(img-0b9entBI-1628506887298)]

百度、字节、美团等大厂常见面试题

【Java核心面试宝典(1),程序员Javaweb源码相关推荐

  1. 【Java核心面试宝典】Day17、详解“Keep-Alive”和“非 Keep-Alive”区别及使用场景

    Hello,你好呀,我是灰小猿!一个超会写bug的程序猿! 用坚持缔造技术.用指尖敲动未来! 和很多小伙伴们一样,我也是一名奔波在Java道路上的"创造者".也想靠技术来改未来,改 ...

  2. 【Java核心面试宝典】Day16、“计算机网络协议层次及服务类型”面试题!✊✊✊

    Hello,你好呀,我是灰小猿!一个超会写bug的程序猿! 用坚持缔造技术.用指尖敲动未来! 和很多小伙伴们一样,我也是一名奔波在Java道路上的"创造者".也想靠技术来改未来,改 ...

  3. HTML5七夕情人节表白网页(抖音-流动爱心表白)HTML+CSS+JavaScript 求婚示爱代码 520情人节告白代码 程序员表白源码 3D旋转相册 js烟花代码 爱心表白网页

    HTML5七夕情人节表白网页❤抖音-流动爱心表白❤ HTML+CSS+JavaScript 求婚示爱代码 520情人节告白代码 程序员表白源码 3D旋转相册 js烟花代码 爱心表白网页 这是程序员表白 ...

  4. HTML5七夕情人节表白网页(流星动画3D相册) HTML+CSS+JS 求婚 html生日快乐祝福代码网页 520情人节告白代码 程序员表白源码 3D旋转相册 js烟花代码 css爱心表白

    HTML5七夕情人节表白网页❤流星动画3D相册❤ HTML+CSS+JS 求婚 html生日快乐祝福代码网页 520情人节告白代码 程序员表白源码 抖音3D旋转相册 js烟花代码 css爱心表白 这是 ...

  5. HTML5七夕情人节表白网页(星空萤火虫) HTML+CSS+JS 求婚 html生日快乐祝福代码网页 520情人节告白代码 程序员表白源码 抖音3D旋转相册 js烟花代码 css爱心表白

    HTML5七夕情人节表白网页(星空萤火虫) HTML+CSS+JS 求婚 html生日快乐祝福代码网页 520情人节告白代码 程序员表白源码 抖音3D旋转相册 js烟花代码 css爱心表白 这是程序员 ...

  6. HTML5七夕情人节表白网页(雪花爱心表白) HTML+CSS+JS 求婚 html生日快乐祝福代码网页 520情人节告白代码 程序员表白源码 抖音3D旋转相册 js烟花代码 css爱心表白

    HTML5七夕情人节表白网页❤雪花爱心❤ HTML+CSS+JS 求婚 html生日快乐祝福代码网页 520情人节告白代码 程序员表白源码 抖音3D旋转相册 js烟花代码 css爱心表白 这是程序员表 ...

  7. HTML5七夕情人节表白网页(抖音超火3D炫酷魔方) HTML+CSS+JavaScript 求婚示爱代码 520情人节告白代码 程序员表白源码 3D旋转相册 js烟花代码 css爱心表白

    HTML5七夕情人节表白网页❤抖音超火❤3D炫酷魔方❤ HTML+CSS+JavaScript 求婚示爱代码 520情人节告白代码 程序员表白源码 3D旋转相册 js烟花代码 css爱心表白 这是程序 ...

  8. HTML5七夕情人节表白网页_生日快乐粒子烟花(自定义文字)_ HTML+CSS+JS 求婚 html生日快乐祝福代码网页 520情人节告白代码 程序员表白源码 抖音3D旋转相册 js烟花代码

    HTML5七夕情人节表白网页❤生日快乐粒子烟花(自定义文字)❤ HTML+CSS+JS 求婚 html生日快乐祝福代码网页 520情人节告白代码 程序员表白源码 抖音3D旋转相册 js烟花代码 css ...

  9. HTML5七夕情人节表白网页(抖音-罗盘时钟) HTML+CSS+JavaScript 求婚示爱代码 520情人节告白代码 程序员表白源码 3D旋转相册 js烟花代码 css爱心表白

    HTML5七夕情人节表白网页❤抖音罗盘时钟❤ HTML+CSS+JavaScript 求婚示爱代码 520情人节告白代码 程序员表白源码 3D旋转相册 js烟花代码 css爱心表白 这是程序员表白系列 ...

最新文章

  1. 报告:AI岗年薪下降8.9%,收入不及2018年
  2. vim php psr2 插件,将vim打造成c++超级ide(vim插件安装)
  3. 201521123087 《Java程序设计》第9周学习总结
  4. GIt版本回退还不会用?轻松学会不怕失误
  5. @async方法不调用了_在Spring中使用Future对象调用Async方法调用
  6. Linux基础之命令练习Day4-fdisk,mkfs,mlabel,mount,umount,mkswap,swapon,dd,top,free,ps,kill,rpm,yum,make...
  7. java基础类的继承_JAVA核心技术I---JAVA基础知识(类的继承)
  8. python self理解_Python列表理解
  9. 特征值_特征值和奇异值的关系是什么?
  10. Newtonsoft.Json.dll 的使用
  11. 排序算法:二分排序(插入排序+二分查找)
  12. 为什么我的世界服务器显示红叉,我的世界藏宝图怎么看红叉
  13. 叔叔阿姨,我真的不会修电脑
  14. 如何对一个水杯进行测试?(详细)
  15. multiprocessing.pool详解
  16. S@Kura的PHP进阶之路(一)
  17. mysql对比时间段百分比_如何使用MySQL计算每日最高价格变化百分比?
  18. ZLMediaKit中_all_track_ready置为true的过程
  19. C语言程序设计博客作业07
  20. 适合编程初学者的 入门级硬件: micro:bit

热门文章

  1. 人脸属性迁移学习笔记
  2. torch.cuda.is_available()返回false
  3. invalid ELF header
  4. pytorch cpu占用较高
  5. Re-ID新方法VPM
  6. libdgl.dll WinError 126
  7. ubuntu 安装phpstorm
  8. opencv2生成图像的梯度图
  9. PMP-【第1章 引论】-2020-12-07(18页-24页)
  10. 八、redis性能测试