HashMap 是由数组+链表/红黑树组成的一种k-v键值对结构。
HashMap 存储数据无序,key 和 value 都可以为 null,key唯一。
HashMap 初始化
在 jdk 1.8 中,HashMap 的构造函数不会创建数组,而是在第一次调用 put() 方法时创建数组,Node[] table 来存储键值对数据。
HashMap 属性
HashMap 默认容量为16。
size HashMap中的元素个数,而不是数组长度。
threshold(临界值) = capacity(容量)* loadFactor(加载因子),size超过临界值就需要扩容。
集合最大容量:1 << 30 2的30次幂
TREEIFY_THRESHOLD = 8; 桶上节点大于这个值且数组长度大于 64 时才会转化红黑树。
HashMap 扩容时,会扩容至2倍。当数组扩容后,索引位置要么是在原索引,要么在原索引+旧数组容量。
HashMap 存储过程
HashMap 进行数据存储时,只计算 key 的 hash 值,根据 key 的 hashcode 来计算路由地址(即在数组中的索引),如果发生 hash 碰撞,使用 key 重写的 equals() 方法与原地址的 key 进行比较,如果相同,则不进行任何操作,如果不相同则插入到原 key 的后面(根据具体情况分析是链表还是红黑树);如果没有发生数据碰撞,就将数据放在数组上。
HashMap 中计算 key hash值的方式
HashMap 底层采用 key 的hashCode 与自己右移 16 位进行按位与运算,保证参与运算的 hash 保存高16位性质。
(h = key.hashCode()) ^ (h >>> 16)
HashMap 路由地址的运算:
其中 n 为数组长度,hash 为 key 计算出的 hash 值。
(n - 1) & hash
构造方法传入数组长度创建 HashMap
如果通过 new 传递的数组长度不是 2 的次幂的话,代码会找到比该值大且最接近该值的2的次幂数设置为数组长度。
该操作会使 n 的二进制最高位往下全部为 1 结束。
int n = cap - 1;
n |= n >>> 1; // ‘|’ 按位或运算,相同位上都为0,才为0
n |= n >>> 2; // ‘>>>’ 无符号右移
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;