HashMap在JDK1.8发生的变化

前言

在开始之前,先说一下文章的讲解顺序:首先,说一下HashMap解决冲突的四个办法之其中一种,剩下三种诸位可以自行了解;其次,说明JDK1.8关于HashMap的变化;最后说明发生这样的变化的原因。
本人程序小白,如有纰漏,希望各位朋友指正。
闲话少说,直奔主题!
在截止到JDK1.7,HashMap一直由数组、链表组成。而到了JDK1.8,则由数组、链表红黑树组成。接下来说一下本文讲解逻辑的一些必备知识点。

必备知识点说明

必备知识点说明中会使用Java底层由数组、链表的常用类来解释提供一个学习Java数据结构的小方向(博主很懒,自己看源码,哈哈哈~)。同时,也会说明一个关于HashMap底层加入红黑树的一个小知识点。

Java中底层使用数组、链表的代表类

数组

在Java集合中,我们所熟知的三个大哥:List、Set、Map,这老三位中底层使用数组实现的就是我们经常使用的ArrayList,Java中的数组大家都知道是使用顺序表的方式实现的,而顺序表的特点就是:查询快,插入和删除慢,至于为什么请看《数据结构》。

链表

同样,在Java集合中,List的其中一个子类与ArrayList完全不同,毕竟龙生九子,子子不同,LinkedList底层实现原理就是链表(PS:毕竟两个儿子都干同一类工作迟早分家……)。链表的实现原理与顺序表不同,它可以理解为动态长度的集合,新来的节点只需要被最后一个节点的指针指向存储单元就连接在一起了(这句话是重点,期末要考)。

阈值

敲黑板,新的知识点。阈值:就是我们常说的临界值,在数学中,可以理解为函数曲线即将转换斜率的位置。而在编程中,我们通常用阈值来描述状态发生某种变化的关键点。(看不明白?没事,后面会解释)。

解决Hash冲突的四种方法(之一)

只讲一种,本文目标不是这个……,撒娇也没用!
单独链表法
简单来说,就是当我们想HashMap中存放数据的时候如果发现地址冲突,那么我们就将当前冲突的存储单元设为头结点,将与原本数据冲突的新加入的数据做为该链表的第二个节点。这就是单独链表法,(图中仅展示了Key存放的方式,不要抬杠还有Value,博主懒得画图……)
在这里插入图片描述
这个时候我们会发现一个问题:链表的遍历效率很低,那么当链表长度超过一定数值后,遍历HashMap的时间复杂度将高的离谱。(能提出这个问题的同学晚饭加鸡腿,看到这还不明白时间复杂度为什么会高的同学晚上和老八一桌)。

JDK1.8使用红黑树作为底层实现的原因

原因

上文说到了,如果使用单独链表法解决冲突的时候,链表长度过长,则会引起整个HashMap遍历的时间复杂度。那么为了解决这个问题,我们在某种条件下将解决冲突的链表结构改为红黑树结构,毕竟树形结构是一个非常优秀的结构,很美(为啥优秀不做解释,认识我的人知道这个问题该去问谁……嘿嘿)。那么所谓的某种条件究竟是什么条件?接下来,有请我们的新产品,啊呸,新知识点登场,上文提到过的阈值。

阈值

在HashMap散列的负载因子为默认值——0.75时,单个Hash存储单元中元素的个数为8个的时候(也就是说某一条链表长度为8时)的概率约为0.00000006,四舍五入后大约为百万分之一,也就是说概率很低。不过,在这时我们遍历这条链表的时候已经开始影响时间复杂度了,所以我们将链表转换为红黑树。
其中要注意的是,树型结构的每个节点占用的存储空间一般来说是链表的两倍左右,所以当冲突数据较少时,链表转化为树形结构无法提现遍历效率,所以才会使用阈值这一概念对结构转化时设定一个条件。
同时,我们也可以将红黑树重新转换为链表,即当阈值为6时,结构将再次变为链表。
至于为什么链表——>红黑树时阈值为8;
红黑树——>链表使阈值为6……,其实是为了避免HashMap解决冲突且当阈值为8时,冲突的结构在链表与红黑树之间反复横跳……。

结尾

阈值其实是Java8中HashMap的作者通过大量实验计算出来的数值,大家只需要知道他的概率计算遵从泊松分布就可以了,
OKK,本篇文章仅仅简单阐述了一下HashMap在JDK1.8发生的变化,如果文章有什么纰漏,希望大家在评论区里一起探讨。

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JDK1.7和JDK1.8之后,HashMap数据结构发生了以下变化: 1. JDK1.7中底层数据结构是由数组+单链表实现,而JDK1.8及之后使用的是数组+链表/红黑树实现。这意味着在JDK1.8中,当链表的深度大于等于8时,会将链表转换为红黑树,以提高查询效率。 2. JDK1.7中的hash值是可变的,因为有rehash的操作,而JDK1.8中的hash值是final修饰的,一旦确定就不会再重新计算。 3. JDK1.7会先判断对象是否是String,如果是,则不采用String复写的hashcode方法,以解决Hash碰撞安全问题。而JDK1.8计算出来的hash值只可能是一个,所以hash值设置为final修饰。 4. 在处理null值方面,JDK1.7中将null视为一个特殊的值,单独处理,而JDK1.8中,null的hash值计算结果为0,与普通的key没有区别。 5. 扩容方式也有所不同。JDK1.7是先扩容后插入新值,而JDK1.8是先插入新值再扩容。 6. 元素插入算法也有差异。JDK1.7采用表头插入法,在扩容时会改变链表中元素原本的顺序,可能导致链表成环的问题。而JDK1.8采用尾部插入法,在扩容时会保持链表元素原本的顺序,避免链表成环的问题。 7. 初始化和扩容基数也有所不同。JDK1.7的初始size为16,扩容时newsize = oldsize*2,而JDK1.8的size始终为2的n次幂。 综上所述,JDK1.7和JDK1.8之后的HashMap在底层数据结构、hash算法、对null的处理、扩容方式、元素插入算法以及初始化和扩容基数等方面都发生变化。\[2\]\[3\] #### 引用[.reference_title] - *1* *3* [JDK1.7和1.8的HashMap有哪些区别?](https://blog.csdn.net/weixin_39098944/article/details/108519553)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [HashMap JDK1.7和JDK1.8的区别](https://blog.csdn.net/qq_47350811/article/details/127327440)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值