栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java知识点整理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java知识点整理

目录

java1.7到1.8HashMap发生的变化

HashMap的put方法

HashMap的扩容

ConcurrentHashMap的扩容机制


java1.7到1.8HashMap发生的变化

1、1.7中是数组+链表,1.8中是数组+链表+红黑树,加红黑树的目的是为了提高HashMap查询和插入的效率以及防止哈希碰撞攻击(输入不同的数据项得到了相同的哈希值)。

2、1.7中链表的插入是头插法,1.8是尾插法,因为1.8中插入key和value时需要判断链表元素的个数,所以就在遍历的时候直接进行尾插法插入。(当链表长度到8时,将链表转为红黑树也是尾插法的一个原因)

3、1.7中的哈希算法比较复杂有各种右移和异或运算(哈希算法越复杂hashcode的散列性越好,hashMap中的元素分布就越均匀,可提高整体效率),1.8中进行了简化,因为有了红黑树来提高效率,节省了哈希算法产生的CPU资源消耗。

HashMap的put方法

1、根据put传过来的key计算key的哈希值(通过哈希算法得到hashcode,再拿hashcode通过与运算操作去和数组的长度减一进行运算得到数组下标)遍历当前链表位置并判断是否有重复key如果有则更新value

2、接着通过得到的数组下标判断:如果该数组下标位置是空,则将key和value封装(1.7封装为Entry,1.8为Node对象)放到该位置。

3、如果该数组位置不为空:

        a、JDK1.7先判断是否要扩容,需要扩容则先扩容再插入,无需扩容则直接将Entry插入当前位置的链表中(头插法)。

        b、如果是JDK1.8则会先进行判断当前位置上的Node是链表node还是红黑树node

  •          当前节点为红黑树节点则将key和value封装为红黑树的node并添加到红黑树中,此过程是一个遍历的过程,遍历的同时进行判断树中是否重复存在当前key,存在则更新value。
  •           当前节点为链表节点,则先将key和value封装为node并通过尾插法插入到链表最后位置,因为是尾插是遍历的过程,在遍历的同时判断是否存在重复key,是否更新value,与此同时可以得知链表的长度,如果大于等于8则将链表转红黑树。
  •            node插入完成之后才进行判断是否进行扩容

HashMap的扩容

JDK1.7新建一个长度为之前数组长度2倍的新数组,然后将当前的Entry数组中的元素全部传输过去,扩容后的新数组是原来的2倍,所以扩容相对来说耗资源

JDK1.8HashMap扩容可以分为三种情况:

第一种:使用默认构造方法初始化HashMap。HashMap在一开始初始化的时候会返回一个空的table,并且thershold为0。因此第一次扩容的容量为默认值DEFAULT_INITIAL_CAPACITY也就是16。同时threshold = DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR = 12。(默认容量*默认负载因子  16*0.75=12)

第二种:指定初始容量的构造方法初始化HashMap。那么从下面源码可以看到初始容量会等于threshold,接着threshold = 当前的容量(threshold) * DEFAULT_LOAD_FACTOR。

第三种:HashMap不是第一次扩容。如果HashMap已经扩容过的话,那么每次table的容量以及threshold量为原有的两倍。

负载因子为0.75的原因:如果更小的话乘以容量后的阈值也会小,这样会存在多余的没用的内存空间,也满足不了哈希表均匀分部的情况,造成分配内存的浪费。如果大于0.75,也就是数组存满了才发生扩容会出现大量的哈希冲突情况出现链表过长,影响get查询数据的效率,因此选择了0.5~1的折中数0.75解决问题。

HashMap是先插入还是先扩容:HashMap初始化后首次插入数据时,先发生resize扩容再插入数据,之后每当插入的数据个数达到threshold时就会发生resize,此时是先插入数据再resize。

ConcurrentHashMap的扩容机制

1.7版本

        1.7版本的 ConcruuentHashMap是基于segment(片段)分段实现的,每个segment相当于一个小型的HashMap,每个segment内部会进行扩容,和HashMap的扩容类似,先生成新的数组,然后将元素转移到新数组中。扩容的判断也是每个segment内部判断的,判断是否超过阈值。

1.8版本

        1.8版本的 ConcruuentHashMap不再基于segment实现,当某个线程进行put时如果发现 ConcruuentHashMap正在扩容,那么该线程就帮助一起扩容,如果put时 ConcruuentHashMap没有正在扩容,那么就会将key-value添加到 ConcruuentHashMap中,并且判断是否超过阈值,超过则扩容。 ConcruuentHashMap支持多个线程同时扩容,扩容前也是先生成一个新的数组(老数组大小的2倍)将旧的数组划分不同组,供不同线程进行负责转移,每个线程负责一组或多组的元素转移工作

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/275199.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号