Java集合
1、hashmap的数组容量一定是2的几次幂
- 在new Hahmap()的时候如果不传递参数,默认的初始化容量就是16
- 如果传递参数,比如传递28,那initcapacity就是28,会走这段代码:(每次左移1位,都乘以2,说明就是2的n次幂)
int capacity=1;
while(capacity<initCapacity)
capacity<<=1;
2、hashmap扩容的边界值是12
- Math.min(capacity装载因子,数组的最大容量+1)求最小值的结果就是160.75=12
- 当数组>12时,数组扩容
3、hashmap存储数据的put方法
- 通过key值获取哈希码h^=k.hascode
- 根据哈希拿到元素在数组的位置h&(table.length-1)相当于h%length
- 添加元素的方法addEntry(当未到达阈值会createEntry,到达阈值会将数组扩容成原来的2倍)
- 当出现哈希碰撞时,7上8下(jdk1.7插入到当前元素之前,当jdk1.8之后会插入到当前元素的后面)(多线程同时操作及逆行扩容时,可能会有循环链表的出现)
- 当出现哈希碰撞时,先比较哈希hash再比较equals(key)值
4、hashmap中计算key的哈希值
jdk1.7
- 通过key获得哈希码的时候,没有直接hascode拿到哈希值
- 而是通过二次散列(通过一个异或的运算h^=k.hascode)
- 核心思想是增加哈希码的不确定性,不会轻易的出现哈希碰撞
h=(h>>>20)(h>>12)等等
jdk1.8时:
4. h=key.hascode()^(h>>>16)也是为了让高16位和低16位同时参与运算,让数据散列更加均匀
5、 扩容的时机
- 第一次put时,数组的长度为null,也会resize扩容
- 数组的长度大于容量*加载因子
- 扩容的大小是左移一位,原来的2倍
6、树化的时机
- 容量大于等于64(当数组的容量没有达到64,会优先选择扩容而不是树化)
- 链表的长度大于等于8
- 为什么大于8的时候会树化?
7、解决哈希冲突的几种办法:
- 再散列法:通过H(key)拿到哈希值出现哈希碰撞后,以这次的H(key)为基础,再次hash,依次类推,直到不出现哈希碰撞为止,再哈希法的哈希表的长度要远远大于需要存放的元素,因为要再哈希,又不能把之前的H(key)删掉,只能做删除标记,不能真正删除结点
- 再哈希法:提供了多种hash方法,第一种hash不行,换第二种