hash三列操作
除法散列法
数组初始容量16
扩容因子:0.75
8-红黑树 泊松分布
6-链表
hashmap在new的时候没有创建数组(数组里面放的是entry对象,这里面含有key,value,next,hash),只是传了2个参数,一个16,一个0.75
如果在初始化的时候,给的数组长度为11,13,17之类的,在put的时候,会有一个转换的过程,将他转为距离它最近的2的n次幂的值
初始值设置成2的n次幂原因:1.为了方便对key的&运算,提高效率,&要比取模运算效率高,hash & (initCapacity-1)
然后再创建数组空间
对key取hash值,为了减少hash碰撞,将二进制值右移四位,散列到数组中
通过&运算,代替取模运算
hashmap 四个参数
1.默认初始容量1<< 4–16
2.容量最大值1<< 30 – 2的30次幂
3.扩容因子0.75 也就是说初始长度为16,则当长度达到16*0.75=12的时候就进行扩容,16变成32
4.链表转红黑树,长度为8,当长度>7时就进行转换 --泊松分布
5.红黑树转链表,长度为6
6.jdk1.7中,数组中放的是entry(hash,key,value),jdk1.8中是node(hash,key,value)和treeNode(left,right,red,prev,parent)
补充:无论初始容量写成多少,到hashmap中都会进行一个转换,转换成距离最近的2的n次幂,进入put()第一件事就是这个
7.1.8中的扰动函数的方式和1.7中扰动方式不一致,对key取hash值,采用key的hashcode 异或 key>>>16,这个叫扰动函数,不然在确定key的存放位置时是通过key%(数组初始长度-1),这样只有低4位参与到运算中,所以通过上面的这种方式让高位也参与到运算中,增加hash散列的程度
8.为什么容量一定是2的n次方,
- 因为2的n次方-1后,低位都是1111,在取模运算时,取得的余数正好就是keyhash值得低4位,采用&运算,&1111,低4位的值正好就是取模的值。
- 在进行数组扩容后,需要重新分配位置,比如扩容到了32,就是11111,只需要对比第5位的值进行&操作,如果是0,表示新数组下标不变,如果是1则将索引位置加上旧的数组长度即可
- 1.7中扩容后链表采用尾插法,16-28-32变成32-28-16,1.8采用头插法
具体执行过程,见牛客网上的详解,已收藏
主要区别:1.扰动函数扰动次数不同,1.7扰动了6次,1.8扰动了2次
2.1.8中如果产生hash冲突,先是链表,后是红黑树