hash函数的设计
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
- 为了让 key 的 hashcode 的高位也参与 hash 运算
为什么扩容是2的整数倍
- 由于 hashcode 前后有 40亿 的大小,内存肯定是不可能放下,所以要有 bucket 数组;
- 在进行 hash 值如何对应 bucket 上的哪个元素时,一般使用 hashcode % length 的方式,但是取余操作比较慢,所以最好用位运算来代替
- 如果采用
hashcode & (length - 1)
可以代替取余操作,但是前提是 length 必须是 2 的倍速 - 如果 n 不是 2的幂,那么 hash() & (n - 1) 时, (n-1) 低位就不全是1,在与运算的时候低位不为1的位置计算后都为0,增大了 hash 的碰撞几率
如何解决 hash 碰撞问题
- 根据 key 二次hash 后根据寻址算法算出 index 后,如果该位置为 null,那么会直接存入,如果该位置有内容,就会出现 hash 冲突
- 链表插入 1.7是表头,1.8是表尾。表头插入会导致死循环的问题,造成CPU百分百。
- 链表长度大于8且数组长度大于64的时候转为红黑树,而在红黑树元素小于6时重新回归链表
使用错误
- 0.75 会扩容,所以设置初始值,不要直接设置 !!! 建议使用 guava 的
newXXXMapWithExpectSize()
方法!!!