HashMap源码解析

本文的所有图片以及源码解析内容都来自于微信公众号<java知音>,原文作者:阿进的写字台。此处仅是对该公众号分享的内容进行一下消化吸收,不作传播。想要阅读原文,可以关注这个公众号。
本文是针对jdk1.8的HashMap源码进行解析,jdk1.7和jdk1.8的HashMap有很大的不同。
jdk1.8的HashMap较jdk1.7增加了红黑树的数据结构,当链表中的Node节点数超过8个,此链表将会转换成红黑树。同样,当树中的节点数低于6个,又会重新转换成链表。
HashMap底层是哈希表结构,即:数组加链表以及红黑树。
哈希表数据结构为:
HashMap源码解析
HashMap采用哈希表的数据结构解决hash碰撞带来的问题。

下面就HashMap的resize过程、put过程、get过程分别做一下解析
resize:
为什么会进行resize?
我们知道,数组在建立时都要指定其长度,所以数组是定长的一种数据结构。需要存储的数据过多时,数据量难免就会超过数组长度。此时HashMap就会进行扩容动作。容量扩充为原来长度的两倍
何时进行resize动作?
当数据量超过阈值(threshold)时或者table容量为0时。threshold=capacityloadFactor,初始化的capacity大小为16,loadFactor为0.75,所以当数据量超过160.75=12时,HashMap就会进行扩容动作,生成一个新的数组,将旧数组的数据拷贝过来,同时将旧数组引用标记为null,指明给垃圾回收器回收该资源。
put操作:
当需要新增一个值时。先通过hash算法(index = (table.length-1) & hash)计算出该值在数组中的下标位置(bucket)。然后判断该位置是否有值。如果没有值,直接就该值放在第一个位置即可。如果该位置有值,且该key的hash值相同并且key equals我们准备存储的key,将value进行替换,同时返回旧值。如果key != 我们准备存储的key,说明发生了hash碰撞。此时我们需要通过e.next访问bucket下面的元素。有可能是链表,也有可能是红黑树。依次遍历。找到该key,替换旧值。如果没有找到旧值,创建新的节点,将该节点按照红黑树的结构或者链表的结构存储,将值存储到链表尾部。
put操作图解:
HashMap源码解析
get操作:
get操作就是根据key获取对应的value,首先根据hash算法计算出该key对应的数组的下标位置,利用hash值和key值判断桶上是否有元素,如果没有元素,直接返回null。如果有元素,判断第一个元素是否是我们之前存储进去的值,如果是,直接返回。如果不是,需要判断首元素的next是否为null,如果首节点没有next节点,直接返回null。如果首节点的next节点不为null。则需要根据next是红黑树或者是链表进行遍历next节点,直到获取到值为止。否则返回null。
get操作图解:
HashMap源码解析