HashMap原理
1、必备运算知识
1、逻辑运算符
- 逻辑与 & 同真为真
- 短路与 && 同真为真 效率高
- 逻辑或 | 有真则真
- 短路或 || 有真则真 效率高
- 非 !
- 异或 a^b 不同则真
2、原码、反码、补码
- 二进制最高位为符号位 0正1负
- 正数 三码合一
- 负数的反码 符号位不变,其他位取反
- 负数的补码 = 反码 + 1
- 0 的反码补码都为0
- 计算机补码运算,原码显示
3、位运算符
- & 按位与
- | 按位或
- ^ 按位异或
- ~ 按位取反
- >> 算术右移 右移n位:本质是除以2
- << 算术左移 左移n位:本质是乘以2
- >>> 逻辑右移 低位溢出,高位补0
2、内部数据结构
链表平均查找长度n/2 红黑树查找长度log(n)
3、数据存储原理
- 判断数组是否为空,为空进行初始化;
- 不为空,计算 k 的 hash 值,通过(n - 1) & hash计算应当存放在数组中的下标 index;
- 查看 table[index] 是否存在数据,没有数据就构造一个Node节点存放在 table[index] 中;
- 存在数据,说明发生了hash冲突(存在二个节点key的hash值一样), 继续判断key是否相等,相等,用新的value替换原数据(onlyIfAbsent为false);
- 如果不相等,判断当前节点类型是不是树型节点,如果是树型节点,创造树型节点插入红黑树中;(如果当前节点是树型节点证明当前已经是红黑树了)
- 如果不是树型节点,创建普通Node加入链表中;判断链表长度是否大于 8并且数组长度大于64, 大于的话链表转换为红黑树;
- 插入完成之后判断当前节点数是否大于阈值,如果大于开始扩容为原数组的二倍
3.1初始容量大小
这个方法保证了HashMap总是使用2的幂作为哈希表的大小
(另:hash值的范围很大,内存放不下。于是对长度取模运算%,但如果除数是2 的幂,那么取模和除数减一的位与运算相同,但是位与运算效率高)
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
3.2HashMap的哈希函数
hash函数是先拿到 key 的hashcode,是一个32位的int值,然后让hashcode的高16位和低16位进行异或操作
static final int hash (object key){
int h;
return (key == null)? 0 : (h = key.hashCode()) ^ (h >>> 16)
}
扰动函数,减少hash碰撞
3.3底层原理
HashMap通过key的HashCode 经过扰动函数处理得出hash值,然后通过(n-1) & hash 判断存放位置。(n指的是数组长度)
其余的看链接