集合
ArrayList
扩容机制
- 无参初始容量:0
如果给的是整数参数的有参构造,以整数为容量
如果给的集合为参数,以集合为大小创建初始容量
add方法扩容
-
第一次扩容:10
add第一个元素,触发第一次扩容
以新的数组替换掉旧的数组,长度为0的就不用了
-
第二次扩容:上一次容量的1.5倍
如:创建一个长度为15的新数组,把旧数组的元素拷贝到里面,把新的元素追加新的数组中去。新数组代替旧数组,无人引用作为垃圾会被垃圾回收。
-
说是1.5其实是右移动一位,第三次扩容
15>>1,正数的右移一位相当于除2+原始容量
15/2=7
7+15=22
扩容:0,10,15,22,33,49,73,109,163,244,366,549...
addAll方法扩容
添加的是Collection集合
运行规律:当原始容量不够时,下次扩容的容量大小跟元素个数二者之间的较大值作为下次容量
-
添加集合小于10时
第一次扩容:10
-
第一次添加集合大于10时
例如11>10,容量11
-
之后添加容量
如:元素有10个的,容量为10,之后调用addAll加3个元素,正常情况下次扩容为15,但只有13个元素
15>13,扩容为15
元素有10个的,容量为10,之后调用addAll加6个元素,正常情况下次扩容为15,有16个元素
16>15,扩容为16
迭代器Iterator_FailFast_FailSafe
遍历集合,可以遍历Set集合遍历List集合,
package com.zyl.list;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @Auther:zhuangyilong
* @Date:2023/3/23
*/
public class FailFastVsFailSafe {
//fail-fast 一旦发现遍历的同事其他人来修改,则立刻抛异常
//fail-safe 发现遍历的同时其他人来修改应当能有应对策略,例如牺牲一致性来让整个遍历运行完成
private static void failFast(){
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("A"));
list.add(new Student("B"));
list.add(new Student("C"));
list.add(new Student("D"));
for (Student student :list){
System.out.println(student);//在这运行途中进行修改
}
System.out.println(list);
}
private static void failSafe(){
CopyOnWriteArrayList<Student> list =new CopyOnWriteArrayList<>();
list.add(new Student("A"));
list.add(new Student("B"));
list.add(new Student("C"));
list.add(new Student("D"));
for (Student student :list){
System.out.println(student);//在这运行途中进行修改
}
System.out.println(list);
}
public static void main(String[] args) {
// failFast();
failSafe();
}
}
Iterator_FailFas源码分析
增强for循环,底层也有用到迭代器,首次循环new了个迭代器对象:
之后每次循环都是调用迭代器的hasnext方法看看有没有下一个元素,调用next方法移动到下一个元素
调用迭代器的构造方法,之后初始化一些成员变量
int expectedModCount = modCount;
记录list集合被修改了多少次
modCount是list集合的成员变量,记录list被修改几次,因为在循环前已经往集合+了4次元素,加一次modCount+1
expectedModCount迭代器的成员变量,记录迭代器对象刚开始迭代时list的修改次数记录
之后调用迭代器hasNext和next
每次调用next时都会调用checkForComodification(),该方法是对ModCount进行检查
迭代器内部记录下来的修改次数和外面list的修改次数做对比,抛出ConcurrentModificationException()并发修改异常,判断循环开始时和循环中间的修改次数是否一致,断定有没有其他人对集合进行修改
乐观锁的感觉
debug在循环过程加元素
modCount变成5
下轮循环调用下轮元素,显示有数据
之后运行到checkForComodification()方法
最后抛出异常
Iterator_FailSafe源码分析
CopyOnWriteArrayList<Student> list =new CopyOnWriteArrayList<>();
ArrayList另一个迭代器实习COWIterator
进入构造方法
es会把当前正在遍历的数组记录下来
会把数组保存在迭代器的snapshot
循环途中往list新增一个元素
进入源码
snapshot还是原来的数组
但外面list 的数组是五个元素
list的数组和迭代器的数组是两个不同的数组。
迭代器里正在循环的是4个元素的最开始的数组,list中是有5个元素的数组。出现两个数组
add方法源码:
add方法里把原来的数组复制一份,长度+1,加在复制出来新数组的最后一个元素,遍历时是旧数组,但每次添加时都是新数组
,二者互不干扰,读写分离。
只是旧数组遍历结束就没用了。
总结
- ArryList是fail-fast的典型代表,遍历的同时不能修改,尽快失败
- CopyOnWriteArrayList是fail-safe的典型代表,遍历的同时可以修改,原理是读写分离
vector是fail-fast