扩充机制
package com.zmz.collection;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author 张明泽
* Create by 2022/5/16 12:24
*/
public class ArrayListDemo {
/**
* ArrayList 初始化长度为 10
* 调用有参构造传多少是多少
* 扩充机制:当前长度进行移位运算 + 当前长度 (10,15,22,33,49,73)
* 二次添加时,若传入的长度大于扩充的长度 (11),结果为: Math.max(扩充的长度,11)
* 二次添加时,只会扩充一次,不会扩到大于传入的长度,案例见下面的情况
*
* size()方法只能查看数组的个数,需要利用反射机制去查看数组容量
*/
public static void main(String[] args) throws Exception {
/**
* 第一种情况
*/
ArrayList<Integer> arrayList1 = new ArrayList<>();
arrayList1.addAll(Arrays.asList(1,2,3));
System.out.println("数组的长度" + getArrayListLength(arrayList1)); // 10
/**
* 第二种情况
*/
ArrayList<Integer> arrayList2 = new ArrayList<>();
// 小于10 长度就扩充为10 大于10 传多少是多少 (本身的容量为10,不会二次扩容)
arrayList2.addAll(Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13));
System.out.println("数组的长度" + getArrayListLength(arrayList2)); // 13
/**
* 第三种情况
*/
ArrayList<Integer> arrayList3 = new ArrayList<>();
arrayList3.addAll(Arrays.asList(1,2,3,4,5,6,7,8,9,10));
// 添加之前先扩容 5 + 10 > 13
arrayList3.addAll(Arrays.asList(11,12,13));
System.out.println("数组的长度" + getArrayListLength(arrayList3)); // 15
/**
* 第四种情况
*/
ArrayList<Integer> arrayList4 = new ArrayList<>();
arrayList4.addAll(Arrays.asList(1,2,3,4,5,6,7,8,9,10));
// 添加之前先扩容 5 + 10 < 16
arrayList4.addAll(Arrays.asList(11,12,13,14,15,16));
System.out.println("数组的长度" + getArrayListLength(arrayList4)); // 16
}
// 查看ArrayList集合容量方法 -- 反射
public static int getArrayListLength(List list) throws Exception{
//获取Class对象
Class c = Class.forName("java.util.ArrayList");
//映射Class对象所表示类的属性
Field field = c.getDeclaredField("elementData");
//设置访问表示为true
field.setAccessible(true);
//返回指定对象上此 Field 表示的字段的值
Object[] object = (Object[])field.get(list);
return object.length;
}
}
迭代器
fail-fast
一旦发现遍历的同时其他人来修改,则立刻抛异常fail-safe
发现遍历的同时其他人来修改,应当能有对应策略,例如牺牲一致性来让整个遍历运行完成
package com.zmz.collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author 张明泽
* Create by 2022/5/16 14:55
*/
public class IteratorDemo {
/**
* fail-fast 一旦发现遍历的同时其他人来修改,则立刻抛异常
* fail-safe 发现遍历的同时其他人来修改,应当能有对应策略,例如牺牲一致性来让整个遍历运行完成
*/
public static void main(String[] args) {
failFast();
}
public static void failFast() {
ArrayList<Student> arrayList = new ArrayList<>();
/**
* add()方法,原数组上继续添加
*/
arrayList.add(new Student("A"));
arrayList.add(new Student("B"));
arrayList.add(new Student("C"));
arrayList.add(new Student("D"));
/**
* 加入断点,student.name.equals("C") 然后在加入一个"E",继续执行时会抛出异常
* modCount是记录被修改次数(add方法执行,这个变量被改变),迭代器创建之前先将 modCount 赋值给 expectedModCount 每次遍历之前先检查 两个变量 相等不相等,不相等抛出异常
*/
/**
* 增强for循环调用的是迭代器
*/
for (Student student : arrayList) {
System.out.println(student.toString());
}
}
public static void failSafe() {
CopyOnWriteArrayList<Student> arrayList = new CopyOnWriteArrayList<>();
/**
* add()方法,上了锁,先复制一份源数组给新数组,在将长度+1并赋值上
*/
arrayList.add(new Student("A"));
arrayList.add(new Student("B"));
arrayList.add(new Student("C"));
arrayList.add(new Student("D"));
/**
* 加入断点,student.name.equals("C") 然后在加入一个"E",继续执行时结果为原来的结果不会抛出异常
*/
/**
* 增加for循环迭代器是 COWIterator,创建时先将数组复制一份给自己的成员变量,遍历时读取变量
* 而 add() 添加时是一个新数组
* 原理: 读写分离
*/
for (Student student : arrayList) {
System.out.println(student.toString());
}
System.out.println(arrayList);
}
}
class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}