知识点一   数据结构

1.jdk1.7以前  数组+链表 

2. jdk1.8数组+链表+红黑树

HashMap的数据结构_时间复杂度

问题一:为什么转成对应的红黑树

链表在进行元素的查找时,它的时间复杂度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是先存储,后扩容。扩容条件只有大于容量*负载因子