知识点一 数据结构
1.jdk1.7以前 数组+链表
2. jdk1.8数组+链表+红黑树
问题一:为什么转成对应的红黑树
链表在进行元素的查找时,它的时间复杂度O(n);
红黑树在进行元素的查找时,它的时间复杂度Ologn
红黑树的查找要优于链表
知识点二 红黑树的特点
1.平衡二叉树
2.最长路径不能超过最短路径的两倍
3.每一条搜索路径中必须有相同的黑色节点
4.任何一条路径中不能存在两个相同或者连续的红色节点
5.所有的节点非黑即红
6.所有的叶子节点都是黑色的
知识点三
并不是每一次插入节点时,都转化为树
链表通过树化变成红黑树,红黑树退化变成链表
树化的条件:
1.单通的长度大于8(链表长度)
2.整体节点大于64(数组长度)
退化:
1.单通的长度小于6(链表长度)
问题二:hashmap的容量为什么是2的n次幂
1.方便&运算(按位与)
2.方便扩容之后元素的移动
知识点四
HashMap<String, String> strMaps = new HashMap<>(); //此时并没有创建数据,只是部分属性赋值 strMaps.put("1","123"); //第一次添加时创建数组
hash算法:保证hash的散列要足够的均匀
HashMap的hash算法如何保证的呢?
扰动函数:h=key.hashcode()^(h>>>16)
key.hashcode() 低位 h>>>16 高位 低位与高位异或
知识点五: HashMap扩容的步骤
1.扩容:创建一个新的entry数组,长度为原来的两倍
2.rehash: 遍历原数组,把所有的entry重新hash到新的数组
问题三:为什么要重新hash,而不是直接复制?
因为长度扩大之后,hash的规则也随之改变
知识点六:链表的插入
JDK1.7采用头插法,扩容后,计算hash,只需要插入链表头部就行。而JDK1.8采用尾插法,如果先扩容,扩容后需要遍历一遍,再找到尾部进行插入。
问题四:为什么在JDK1.8中进行对HashMap优化的时候,把链表转化为红黑树的阈值是8,而不是7或者5呢?
根据注释中写到,理想情况下,在随机哈希码和默认大小调整阈值为 0.75 的情况下,存储桶中元素个数出现的频率遵循泊松分布,平均参数为 0.5,有关 k 值下,随机事件出现频率的计算公式为 (exp(-0.5) * pow(0.5, k) /factorial(k)))大体得到一个数值是8,那么退化树阀值为什么是6?如果退化树阀值也是8,则会陷入树化和退化的死循环中。如果退化阀值是7,假如对hash进行频繁的增删操作,同样会进入死循环中。如果退化树阀值小于5,我们知道红黑树在低元素查询效率并不比链表高,而且红黑树会存储很多索引,占有内存。所以退化阀值设为6比较合理
知识点七 : hashmap的扩容条件
JDK1.7中扩容条件:当前数量大于 容量* 负载因子 并且数组下标的值不为空,即假如新插入的数据位置在一个数组位置而不是链表上,则插入成功而不扩容
JDK1.8是先存储,后扩容。扩容条件只有大于容量*负载因子