PS:本博客均采用上文字下图片的方式进行描述,深入到源码进行讲解,如有问题,欢迎提出,本博客参考和整合了某站视频和一些博客的论述,希望你能从这篇博客中学习到什么。
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射,键值对的集合,源码中每个节点用Node<K,V>表示,Node是一个内部类,这里的key为键,value为值,next指向下一个元素,可以看出HashMap中的元素不是一个单纯的键值对,还包含下一个元素的引用。
我们知道,HashMap是数组+链表+红黑树的数据结构,所谓的扩容就是对数组的扩容,数组上的元素包含链表结点Node,以及在一定条件下转为的红黑树结点TreeNode,下图就是正常情况下的HashMap:
流程是要走一下的,这里将不多做HashMap的赘述,直接上源码:
先来看看一些HashMap定义的默认参数:
首先,当我们new一个HashMap实例,调用它的默认无参构造函数时,下图1.2是HashMap的构造函数,我们会发现,里面只有一条语句,就是设置了一个加载因子为默认的0.75,其他什么事也没有干,你构造的时候hashmap不会进行初始化,当你调用put函数进行插入时,HashMap才会进入resize方法进行初始化,过程包括赋初始默认值(数组长度16)和默认阈值(数组长度 * 默认加载因子 = 12)如下图1.1,这就是HashMap的懒加载机制。
图1.1
图1.2
于是,当我们调用put方法开始插入数据时,传入key键和value值,此时我们进入put方法,首先会判断table表是否为空,为空则调用resize方法进行初始化。
在初始化时,通过传入的key值在方法hashCode方法生成哈希值,然后在扰动函数hash中(下图1.3)对得到哈希值进行二次加工,这里为什么要进行二次加工呢?因为HashMap的散列性,我们要将元素较为均匀的放在数组上,尽量的减少hash冲突,而得到的hash值是位数较高的,在后面和数组长度相与的操作中高位基本上使用不到(因为数组长度一般不会很大,二进制位数和哈希值的位数相差很大),所以我们要对哈希值的高低16位进行异或运算,让哈希值的高位也参与到运算之中,更加保持HashMap的散列性,这里生成的就是加工后的hash值了。
图1.3
得到hash值后,对传入的Entry对象包装成链表Node,图1.4是Node类的字段,包含传入的key,value值,生成的hash值,和链表的标志next字段。