HashMap的put操作源码学习
一、HaspMap集合简介
基于哈希表的 Map接口的实现。这种实现提供了所有可选的Map操作,并允许 null值和 null关键。(这 HashMap类大致相当于 Hashtable,除了它是不同步的,允许空值。)此外hashMap中的映射不是有序的。
在jdk1.8以前,HashMap由数组和链表组成,数组是HaspMap的主体,而链表则是为了解决哈希冲突而引入的结构。而在jdk1.8以后,HashMap引入了红黑树的数据结构,当链表的长度大于默认值8,并且当前数组的长度大于64时,当前索引位置的所有数据改为红黑树存储,引入红黑树的目的其实也是为了提升HaspMap的效率。
这里我们总结一下Map特点:
1、存取是无序的。
2、键值对都可以存储null,但是键位置只能有一个null
3、键位置是唯一的。
4、数据结构:链表+数组+红黑树(1.8以后的版本)
5、边界值>8 && 数组长度>64,链表转红黑树。
二、HashMap的数据结构
HashMap的数据结构:
jdk 1.8之前:数组+链表
jdk 1.8之后:数组+链表+红黑树
2.1 put操作
我们先来看一下HashMap的整个存储过程结构图:
我们在创建HashMap集合对象的时候,在jdk1.8之前,会在构造函数中创建一个长度是16的Entry[] table 用来存储键值对数据,而在1.8以后,构造方法中不再创建数组,而是在第一次调用put方法时创建数组,Node[] table用来存储键值对数据。赋值过程是在resize()方法中。
如下图所示:
HaspMap的hash算法采用的是无符号右移+按位异或的操作,我们思考一下,为什么会做这样的设计呢?其实也不难理解,设计者肯定是希望我们计算出来hash值尽量的分布均匀,而如下图所示:这种将高位与低位的异或算法正是可以达到hash值分布均匀的效果。
在hashMap中,当我们在进行put操作时时候如果计算出来的hashCode一样时,这个时候我们会判断它的equal值是否相等,相等则覆盖掉原来存储的对象,不相等则使用链表结构储存,当我们的链表长度大小超过8,并且数组的长度大于64的时候,这个时候就会将链表转成红黑树。如下图所示:
2.2 扩容机制
HashMap 的大小默认初始值是16,并且默认的加载因子是0.75,当我们的存储数据占用的空间超过当前容量大小*0.75的时候,这时候就会触发扩容,扩容的规则:每一次扩容,容量增加一倍。
2.3 继承关系
下图是我们HashMap的一个简单的继承关系图:克隆接口和序列化接口我们这里不做概述,HashMap的大部分功能都已经被它的父类AbstracMap实现了,接口Map则给我们提供了常用的操作接口。
HashMap提供了很多的构造器,但是基本上我们自己在使用的时候都是使用的默认构造器,有参构造器其实就是设置初始化容量和加载因子的值,当我们在使用HashMap之前知道我们所存储对象的数量时,选择有参构造可以避免不必要的扩容从而提升我们集合存储的效率。在设置初始容量时,也是有讲究的,我们初始容量必须满足2^n,即使用户设置不满足这个规则,HashMap在后台也会自动匹配一个与其最相近的2的n次幂。如下图所示,当用户设置不满足2的n次幂时,tableSizeFor()方法会匹配最相近的2的n次幂。