简单解析jdk1.7 与jdk 1.8 hashmap 源码以及优化点

jdk1.7  hashmap 源码总结

数据结构,采用数组加单向链表

以put  方法为例子

1.默认变量

默认初始化容量16 -》1<<4

最大容量 2^30 

负载因子0.75

空entry数组 Entry[K,V] table ={}

2.put()方法代码流程

1). 初始化table 数组,如果不传初始化容量,默认16 ,put 方法中 如果传5、12,数组会扩容成大于本数值的最近的2的n次幂 扩容成8、16 原理是右移或算把 最高位1 的后面都变成1   例如 11111    在右移一位0000 1111 相减 = 0001 0000 

2.hash(key)  原理是一次扰动 右移异或,减少hash碰撞的概率

3.hash 获取的值与数组长度减一 与运算,获取数组下标

4.如果该数组下标链表为空直接插入,不为空则环变量链表,通过equal 方法判断链表中是否有相同key 的元素,有直接覆盖,没有的话,采用头插法。把 数组中存储的链表地址赋值给新增的entry 的next 属性在把新增的entry赋值给该数组下标

  • 当调用4的时候之前,就会判断是否需要扩容(扩容的条件是超过阈值以及数组下标的不为空),如果需要扩容,就重新计算hashcode,同时也需要重新定位元素的位置,扩容的时候会将旧数组中的元素迁移到新数组中,扩容后数据是颠倒的,同时也会把同一链表中的元素拆分。原位置和老数组长度加+原位置的位置 例如 7  --》 7 或者7+ 8 =15 

jdk1.8  hashmap 源码总结

 数据结构,采用数组加单向链表+红黑树

以put  方法为例子

1.默认变量

默认初始化容量16 -》1<<4

最大容量 2^30 

负载因子0.75

空entry数组 Entry[K,V] table ={}

2.put()方法代码流程

1). 初始化table 数组,如果不传初始化容量,默认16 ,有参构造方法如果传5、12,数组会直接扩容成大于等于原来数值的2的n次幂

2.hash(key)  原理是2次扰动 高低位异或,使得hash更加均匀

3.hash 获取的值与数组长度减一 与运算,获取数组下标

4.如果该数组下标链表为空直接插入,不为空则环变量链表,通过equal 方法判断链表中是否有相同key 的元素,有直接覆盖(入参的值为false(默认false) 或者为null 会覆盖,其他情况不会覆盖原值),没有的话,采用尾插法。如果链表长度超过8 但是数组长度没有超过64 和等于64  ,则先扩容,链表拆分。如果长度超过了8和数组》=64时 转换为红黑树

  • 当调用4的时候之后,当数组容量未达到64时,以2倍进行扩容,超过64之后若桶中元素个数不小于7就将链表转换为红黑树,但如果红黑树中的元素个数小于6就会退化为链表,扩容后数据是不会颠倒的

1.7和1.8 的区别

1)1.8增加了红黑树,为了数据查询更快,链表太长,要遍历查询太慢

2)1.8中resize 方法包括了扩容和初始化 ,1/7 用两个方法分别做的,作用优化代码,更加直观

3)1.8 采用的是尾插法。1.7使用的是头插法 为了解决多线程循环链表的问题

4)1.8的扩容后的分散是通过hash & 老容量大小==0进行分散的,1。7 是从新算下标,和更改hash种子——扰动函数,也就是自己配置容量大于扩容的容量时候才会更改hash种子 使hash的更加均匀的方式

5)扩容后1.8 的链表顺序不会改变,1.7 链表还是颠倒的。不是颠倒的顺序的。

6)在扩容的时候:1.7在插入数据之前扩容,而1.8插入数据成功之后扩容。

7)1.8的hash 更加散列,,高位异或运算。1.7 是多次的移位运算

 

posted @ 2022-08-27 12:02  potent_prince  阅读(130)  评论(0)    收藏  举报