一、 HashMap中put方法的过程
HashMap基本的存储结构:数组+链表(JDK7之前),数组+链表+红黑树(JDK8及以后)。HashMap中采用的哈希冲突解决办法是链表法(Python中是开放定值法)
首先需要通过当前插入key1的hashcode方法计算得到一个哈希值1,再通过某种算法(hash())得到哈希值2,最后通过另外一个算法(indexFor())之后就得到当前key1和value1应该在数组中存放的位置,然后进入下面的过程
1.1 如果当前位置没有元素,则添加成功,直接在当前数组的位置添加(key1,value1)
1.2 如果当前位置有元素,则比较当前key1和已经存在的key2的hashcode是否相同
2.1 如果两个hashcode不同则在链表中添加成功
2.2 如果两个hascode相同则需要比较两个key的equals方法,注意是调用key1(新添加)的equals方法,key2(已经存在的)作为参数
3.1 如果equals方法为false,则添加成功
3.2 如果equals方法为true,则说明两个key完全相同,而HashMap的key是无序且不允许重复的,所以(key1,value1)会替换(key2,value2)
JDK7和JDK8中put的区别:
1.在上面2.1和3.1中都出现了用链表存储两个元素的情况,在JDK7之前是用头插法,JDK8及之后是用尾插法
2.JDK8采用延迟初始化的操作,即在创建HashMap的时候没有初始化里面的数组,而是在第一次添加元素时初始化(长度为16的数组),同样的操作也出现的ArrayList的初始化中
3.JDK8中单个链表元素数量比较大的时候采用了红黑树,具体过程在扩容机制中讲解
二、HashMap中的扩容机制
控制HashMap中数组大小的因素有两个,一个是初始化长度(默认16),另一个是加载因子(默认0.75)
当数组中元素个数大于长度乘以加载因子的时候(16 * 0.75=12)就会扩容为原来的两倍
JDK8之后红黑树的加入:当一个链表的元素个数大于8而且数组长度大于64的时候会将链表转换为红黑树( treeifyBin()),如果数组长度没有大于64则会认为是数组长度不够导致一个位置的链表元素太多,所以优先触发扩容机制。如果一个红黑树中的元素个数小于6之后,红黑树会退化为普通链表
为什么要使用红黑数:可以将红黑树和平衡二叉树做比较,平衡二叉树要求左右子树的深度之差不超过1,这个要求比较严格,实现成本比较大,而红黑树要求没有这么严格,实现成本更小且可以有效提高查询等操作的效率,其作为一种比链表更为复杂的数组结构,在数据进行添加、删除、查寻等操作更快。
注意在红黑树存储中是按hashcode进行排序的,和compare那一系列没有什么关系,实现compareTo方法或者传入comparator是在TreeSet和TreeMap中需要做的事情
为什么不一开始就使用红黑树:在元素个数不多的时候红黑树对查寻效率提升不大,而且红黑树占用的空间更大,所以没有必要