1.ArrayList扩容规则
知识点1:无参构造器的初始值为0
知识点2:第一次扩容就会创建一个长度为10的数组
第二次后扩容就会是上次的1.5倍(第二次数组长度就是10*1.5=15
注意:新的数组会代替旧的数组,旧的数组会被垃圾回收掉)
第二次扩容计算:
15>>1
7
15+7
22
前20次扩容的数组长度的打印:
addAll():
1.list如果小于10,第一次扩容是10;
2.扩容规则:下次扩容的大小和数组的长度中取较大值;
总结:
迭代器:
failSafe:
重复刚才的过程:
**34-35 .FailFast源码分析
2.LinkedList
ArrayList:是基于内存存储连续存储 可以通过简单的计算可以算出内存地址
比如:第一个元素地址是0 每个地址都是30个字节 第四个下标的地址是:0+4*30=120
LinkList:比如也找到4,先从1找到2的地址,然后从2找到3的地址,从3找到4的地址;
**36.04.00 -05.26 讲解源码
随机查询:AraryList性能更好
后面一样查询性能
LinkedList中间插入慢的原因:查找需要一个一个找到,极其消耗性能
ArrayList头部插入或者其他非尾部查询需要复制一份,然后进行数据替换,性能比较慢
** 38 空间占用原理讲解
3.HashMap
3.1 HashMap的实现原理
计算原则:a 进行hashcode 97,然后二次hash得到 97 ,然后将97 和 数组长度16 进行取模得到1
这时候1就是a的下标
ArrayList获取a需要遍历查询,这种情况HashMap效率高的多
3.2 链表过长的解决方案
链表过长的解决方案:
1.扩容 当数组大于3/4被使用就会被扩容(数组长度是16的话,链表有13个元素就会被扩容
注意:扩容之后下标需要重新计算 如图1)
2.红黑树
需要同时满足两个条件:1.链表长度大于8 2.数组容量必须大于64 如图2
红黑树特点:是二叉树,左边小右边大,时间复杂度是O(logn),效率比链表高
注意:链表查询的时间复杂度O(n) 链表(node)占用的内存没有红黑树(TreeNode)多
正常情况下链表的长度不会大于8,主要防止黑客恶意攻击影响整个系统的性能 如图3
红黑树何时退化成链表:
图1.
图2.
图3.
图4.
退化成链表:
图5
3.3 索引是如何计算的
分布比较均匀的情况(正例):
分布不均匀的情况(反例):
原因:这些数据第一位都相同导致对16进行取模得到的数字都一样 解决方案:以jdk1.8为例
添加二次hash后,反例变均匀:
图6.
3.5 HashMap1.7和1.8的不同
注意:扩容是添加完之后进行扩容
注意:1.7扩容的问题:扩容后遵循 头插法导致扩容之后顺序变了,这就会导致死链问题
1.8在扩容计算node索引时,会优化:
按位与计算:如何是0则不用动位置,如果不为0则计算新的位置旧的位置+按位索引
3.6 HashMap负载因子的设计
注意:类型设置为Thread
注意:多线程很有可能出现丢数据的问题,如上图这两个都有可能同时运行631行代码,导致前面的key被覆盖。
**p53.1.7死链
注意:1.字符串如果多个可以通过这个散列公式计算出对应的hashcode,得到足够混匀的hash值
2.31优化更好可以用按位与进行运算