扩容机制:
- ArrayList()会使用长度为0的数组
- ArrayList(int initialCapacity)使用指定容量的数组
- public ArrayList(Collection extends E> c)会使用c的大小作为数组容量
- add(Object o) 首次扩容为10,再次扩容为上次容量的1.5倍
- addAll(Collection c) 没有元素时,扩容为Math.max(10,实际元素的个数),有元素时Math.max(原容量1.5倍(实际是原容量>>1+原容量),实际元素的个数)
Iterator 的fail-fast
-
ArrayList是fail-fast的典型代表,遍历时不能修改,尽快失败
-
一旦发现遍历的同时有人来修改,则立即抛异常(下一轮循环时报错—并发修改异常)
private static void failFast() {
ArrayList
-
增强for循环底层开始先创建一个迭代器()
-
初始化迭代器的变量,其中的 : int expectedModCount = modCount用来记录集合被修改了多少次
- modCount:是list集合的变量,记录集合倍修改的次数
- expectedModCount :迭代器的变量,记录迭代器刚开始迭代时的修改次数
-
然后调用迭代器hasNext()元素判定有没有下一个元素,next()来移动到下一个元素
-
在next()方法中都会调用checkForComodification()方法:把迭代器一开始的修改次数跟我们外面list修改次数进行对比。如果相等,什么都不会做,若不相等就抛异常
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
-
-
Iterator 的fail-safe
-
CopeOnWriteArrayList是fail-safe的典型代表,遍历时运行修改,原理是读写分离
-
发现遍历的同时有人来修改,应当能有应对策略,例如牺牲一致性让整个遍历运行完成(就是当前能继续遍历,输出的是修改前的,下次遍历输出的就是修改后的)
private static void failSafe(){
CopyOnWriteArrayList
-
底层依然是先创建一个迭代器对象
return new COWIterator
(getArray(), 0); -
在这个迭代器构造方法内部会把当前正在遍历的数组记录下拉来
snapshot=es
-
在给list添加元素时,会copyof一个新数组数组,添加的元素添加到了复制出来的新的数组里。遍历时用的是旧数组,添加时都会复制出一个新数组,两者互不干扰
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZxWXVBKH-1652707084770)(C:Users展前进AppDataRoamingTyporatypora-user-imagesimage-20220516204317645.png)]
ArrayList vs LinkedList
-
ArrayLsit
-
基于数组,需要连续的内存
-
随机访问快(根据下标访问)
-
尾部插入、删除性能可以,其他部分插入、删除都会移动数据,因此性能会低
-
可以利用cpu缓存,局部性原理
(cpu缓存读取时会把相邻的元素也读进去,备用)
-
-
LinkedList
- 基于双向链表,无需连续内存
- 随机访问慢(要沿着链表遍历)
- 头尾插入删除性能高;(中间由于需要先沿着链表找到指定位置浪费大量时间)
- 占用内存多
(因为它里面是有一个个Node对象组成,每个Node又分成元素,上一个指针,下一个指针等等)



