简单解析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 是多次的移位运算

浙公网安备 33010602011771号