ArrayList源码学习
一.介绍
- ArrayList是个动态数组,实现了List接口,是用来存储数据的容器之一,底层的数据结构是数组。
- 如果存储基本类型的数据,如int,long,boolean,short,byte,那只存储它们对应的包装类。
- ArrayList是线程不安全的。
- 增删慢、查询快
二.源码部分
1.基本参数
//RandomAccess:随机快速访问接口
//Cloneable是标记型的接口,它们内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就会抛出CloneNotSupportedException
//Serializable:对象的序列化处理
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
//序列化唯一标示 serialVerisionUID
private static final long serialVersionUID = 8683452581122892189L;
//默认容量 10 字节
//static和final是控制类成员变化的修饰符。
//使用final关键字可以声明类、成员变量和成员方法,一经声明,便不可继承、不可修改和不能覆盖
private static final int DEFAULT_CAPACITY = 10;
//空数据数组
private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
//默认空容量的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
//实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
transient Object[] elementData;
private int size;
//最大数组长度
private static final int MAX_ARRAY_SIZE = 2147483639;
2.构造函数
/**
* 构造函数
* @param initialCapacity
*/
public ArrayList(int initialCapacity) {
//如果传入的参数大于0,则构造一个参数大小的数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else {
//如果传入参数小于0,则抛出异常
if (initialCapacity != 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
//参数为0,创造一个空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
//无参构造函数,构造一个初始容量为0的数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//?:任意类
public ArrayList(Collection<? extends E> c) {
//toArray() 方法将 Arraylist 对象转换为数组
Object[] a = c.toArray();
//如果传入数组的长度不为0,
if ((this.size = a.length) != 0) {
//判断数组类型是否为object
if (c.getClass() == ArrayList.class) {
//是的话,将其指向传进来的数组
this.elementData = a;
} else {
//转换为object对象
this.elementData = Arrays.copyOf(a, this.size, Object[].class);
}
} else {
//长度为0则数组为空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
3.扩容机制
/**
* arraylist扩容机制
* arraylist有三种构造函数:无参,用户指定大小和指定集合元素的列表
* 无参:初始化为空数组,当调用时扩容为10
* @param minCapacity
*/
//ensureCapacity() 方法用于设置具有指定容量大小的动态数组。
//minCapacity - 动态数组的容量
public void ensureCapacity(int minCapacity) {
//1`minCapacity > this.elementData.length 设置的容量大于数组容量
//2`this.elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA 不为无参构造函数
//3.minCapacity > 10
if (minCapacity > this.elementData.length && (this.elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA || minCapacity > 10)) {
++this.modCount;
this.grow(minCapacity);
}
}
//数组扩容
private Object[] grow(int minCapacity) {
//copyOf()方法传回的数组是新的数组对象
//copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值
return this.elementData = Arrays.copyOf(this.elementData, this.newCapacity(minCapacity));
}
private Object[] grow() {
return this.grow(this.size + 1);
}
private int newCapacity(int minCapacity) {
int oldCapacity = this.elementData.length;
//新的容量相当于原来的原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//数组size>=4进入下面函数
if (newCapacity - minCapacity <= 0) {
//若arraylist为默认空数组,返回10或所需值
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(10, minCapacity);
} else if (minCapacity < 0) { //所需值小于0,抛出异常
throw new OutOfMemoryError();
} else {
return minCapacity;
}
} else {
//检查新容量是否大于最大容量
return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
}
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) {
throw new OutOfMemoryError();
} else {
//判断所需容量是否在指定范围内
return minCapacity > 2147483639 ? 2147483647 : 2147483639;
}
}
4.add方法
//arraylist.add(int index,E element)
private void add(E e, Object[] elementData, int s) {
//如果数组的长度满了,扩容
if (s == elementData.length) {
elementData = this.grow();
}
//赋值
elementData[s] = e;
//size+1
this.size = s + 1;
}
//.add(E element)时
public boolean add(E e) {
//记录了ArrayList结构性变化的次数
++this.modCount;
//在arraylist最后插入
this.add(e, this.elementData, this.size);
return true;
}
//.add(int index,E element)元素插入指定位置
public void add(int index, E element) {
//add操作越界检查
this.rangeCheckForAdd(index);
++this.modCount;
//?
int s;
Object[] elementData;
//扩容检查
if ((s = this.size) == (elementData = this.elementData).length) {
elementData = this.grow();
}
//index后的数据复制过来
System.arraycopy(elementData, index, elementData, index + 1, s - index);
//插入
elementData[index] = element;
this.size = s + 1;
}
/**
* public static void arraycopy(
* Object src, //源数组
* int srcPos, //源数组的起始位置
* Object dest, //目标数组
* int destPos, //目标数组的起始位置
* int length //复制长度
* )
**/
5.其他方法
//缩小长度
public void trimToSize() {
//modCount是这个list被结构性修改的次数
++this.modCount;
if (this.size < this.elementData.length) {
//copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值
this.elementData = this.size == 0 ? EMPTY_ELEMENTDATA : Arrays.copyOf(this.elementData, this.size);
}
}
//indexOf ()的意思:查找一个字符串中,第一次出现指定字符串的位置。
public boolean contains(Object o) {
return this.indexOf(o) >= 0;
}
public E get(int index) {
//index是否被包含于[0,length),true返回值为:Index
Objects.checkIndex(index, this.size);
return this.elementData(index);
}
public E set(int index, E element) {
//越界检查
Objects.checkIndex(index, this.size);
E oldValue = this.elementData(index);
this.elementData[index] = element;
return oldValue;
}
三.总结
- EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的异同
1)DEFAULTCAPACITY_EMPTY_ELEMENTDATA:无参构造函数创建,当第一次执行add操作时
数组扩容为10
2)EMPTY_ELEMENTDATA则加1
- ensureCapacity(int minCapacity)方法:
1)当设置的minCapacity>容量且不是无参构造函数构造的数组或者minCapacity>10,才会去调用扩容的方法
2)所以当数组为DEFAULTCAPACITY_EMPTY_ELEMENTDATA时用这个方法要设置的值>10才会实际扩容
- add相关
1)有参构造函数数组,容量大于4开始进行1.5倍扩容
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)