HashMap实现了Map接口,用于保存键值对,其底层是使用数组+链表+红黑树实现的
存储结构是内部包含了一个Entry类型的数组table,Entry存储着键值对,从他包含的next字段可以看出,Entry是一个链表,数组中每个位置被当成一个桶,一个桶存放一个链表,HashMap使用拉链法解决冲突。
HashMap的put方法过程如下:
(1)如果table没有初始化就先对他初始化
(2)使用hash算法计算key对应的索引
(3)判断索引处有没有存在元素,没有就直接插入
(4)如果索引处存在元素,就有两种情况,一种是链表形式,就直接遍历到链表尾部插入,如果是红黑树,就按红黑树结构插入
(5)如果链表的数量大于阈值9,就转化为红黑树
(6)添加完成后检查是否需要扩容
详细讲一下扩容:
当往HashMap中放入元素时,如果元素个数大于threshold时,会进行扩容,使用2倍容量的数组替代原数组 (threshold = 容量*装载因子)
为什么要用2倍容量?是基于什么考虑的?
由于数组的容量是2的幂次方扩容的,那么在扩容时,新的位置要么在原位置,要么在原位置+原位置的位置 为什么呢?
原因是数组长度变为原来的2倍,表现在二进制上就是多了一个高位参加下标计算,也就是说,元素拷贝过程中不需要重新计算元素在数组中的位置,只需要看看原来的hash值新增的那个bit是1还是0,是0的话索引没变,是1的话索引变为“原索引+OldCap”(根据e.hash&(oldCap-1)==0判断) 这样可以省去重新计算hash值的时间,而且由于新增的1bIt是0还是1可以认为是随机的,因此resize的过程会均匀的把之前冲突的节点分散到新的桶