概念:
ArrayList是Java集合框架中的一个类,它继承于AbstractList, 实现了List接口。是一个动态数组(空间大小可变),可以存储任意类型的对象。 实现了RandomAccess接口 ,可以对元素进行快速访问。 实现了Serializable接口,说明ArrayList可以被序列化,还有Cloneable接口,可以被复制。
特点:
-
容量的可扩展性:空间大小是可以动态增长的,它会根据需要自动调整内部数组的大小。
-
随机索引访问:由于ArrayList是基于数组实现的,因此可以通过索引直接访问元素,提供了良好的随机访问性能。
-
可变性:ArrayList支持添加、删除和修改元素,可以根据需要对列表进行动态修改。
-
有序性:元素在ArrayList中的顺序是有序的,就像在数组中一样。可以根据索引来获取和操作元素。
-
允许重复元素:ArrayList允许存储重复的元素。
-
线程不安全
-
可以存储任意引用类型(Object)的对象, 并且存储的必须是对象(引用类型),不能存储基础数据类型,基本数据类型通过装箱为包装类数据进行存储。。
源码分析:
因为源码语句很多,这里只分析常用方法的方法代码(通过测试类中的方法语句ctrl+鼠标左键查看源码)
测试用例:
import java.util.*;
/**
* @author felix
* @created 2023/7/19
*/
public class Test {
public static void main(String[] args) {
ArrayList<Integer> wc = new ArrayList<>();//无参构造
ArrayList<Integer> yc = new ArrayList<>(6); //有参构造
wc.add(1);//添加元素
wc.add(2);
wc.add(2);
// System.out.println(wc);
yc.add(5);
yc.add(6);
wc.add(0,3);//在指定位置插入
// System.out.println(wc);
wc.addAll(yc);//将指定 集合 中的所有元素添加到此列表的尾部。
wc.addAll(0,yc);//指定的位置开始,将指定 集合 中的所有元素插入到此列表中。
// System.out.println(wc);
wc.set(1,9);//用指定的元素替代此列表中指定位置上的元素。
// System.out.println(wc);
wc.remove(1); //移除此列表中指定位置上的元素。
wc.remove(Integer.valueOf(1)); //移除列表中首次出现的指定元素(如果存在)
// System.out.println(wc);
wc.removeAll(yc);
wc.get(0);
System.out.println(wc.size());;//打印空间大小
}
}
ArrayList的底层实现
ArrayList底层采用Java数组来存储集合中的内容,这个数组是Object类型,它的操作基本上都是基于对数组的操作。
transient Object[] elementData; //ArrayList的底层实现
//访问级别为包内私有,使内部类能够访问到其中的元素
//transient 是java关键字,为变量修饰符。
//用transient声明一个实例变量,可以关闭关闭serialization持久化对象实例的机制,避免对象被序列化。这样,当对象存储时,它的值不需要持久化。
//Object[] elementData 是ArrayList容器,ArrayList的基本操作都是基于elementData变量来进行的。
一些静态常量
//一个序列化版本号,用于控制Java序列化和反序列化的过程中的版本兼容性
private static final long serialVersionUID = 8683452581122892189L;
//数组默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//为了对应不同的构造函数(有参构造、无参构造),ArrayList使用了不同的数组。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//提供给无参构造函数使用的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//提供给有参构造函数使用的空数组
//数组最大容量值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造函数
无参构造:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //调用无参空数组初始化 elementData对象
}
有参构造:
public ArrayList(int initialCapacity) {// initialCapacity数组容量
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;//调用有参空数组初始化 elementData对象
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
集合构造的构造函数:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size,Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
add()方法
//直接在列表末尾添加
public boolean add(E e) {
ensureCapacityInternal(size + 1); //保证内部容量
// Increments modCount!! 每次增加操作后modCount都会递增
elementData[size++] = e;//在数组末尾赋值
return true; //是否成功
}
//modCount是一个在Java集合框架中常见的属性,用于追踪集合的结构性修改次数。它用于在迭代器遍历中检测并发修改异常
//在指定下标位置添加
public void add(int index, E element) {
rangeCheckForAdd(index); //对添加操作进行索引范围检查,确保在执行添加操作时,所指定的索引处于有效的范围内
ensureCapacityInternal(size + 1); //保证内部容量--扩容
System.arraycopy(elementData, index,
elementData, index + 1,size - index); //数组拷贝,将原本的elementData中的index下标位置后的所有元素复制,在index+1位置放入复制的内容。
//数组拷贝后再将index下标位置的元素的值替换为element,就完成了插入
elementData[index] = element;
size++;//这里的size只是记录列表长度
}
//rangeCheckForAdd
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)//判断索引是否在size范围内,不在就抛出异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//System.arraycopy 在不同数组之间进行元素复制。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,int length);
//src:源数组,即要复制元素的数组。
//srcPos:源数组的起始位置,从该位置开始复制元素。
//dest:目标数组,即要将元素复制到的数组。
//destPos:目标数组的起始位置,从该位置开始粘贴复制的元素。
//length:要复制的元素数量。
ensureCapacityInternal方法:
保证数组的空间容量足够
//保证数组内部容量
private void ensureCapacityInternal(int minCapacity) {//size+1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
//
}
//计算数组容量
private static int calculateCapacity(Object[] elementData, int minCapacity){
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//判断是否是无参构造,是无参构造,则返回默认值或设定的最小容量(size+1)
return Math.max(DEFAULT_CAPACITY, minCapacity);//设定长度小于默认值则返回默认值(默认值见静态常量)
}
return minCapacity; //有参构造直接返回最小容量
}
//确定这个容量能满足
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)//需要的最小容量比你集合中底层实现数组的长度还长,不能保证容量,需要进行扩容
grow(minCapacity);//扩容
}
//数组扩容
private void grow(int minCapacity) {//10
int oldCapacity = elementData.length; //创建临时变量,存储数组长度旧值
int newCapacity = oldCapacity + (oldCapacity >> 1);//1.5倍扩容
//oldCapacity >> 1 等价于 oldCapacity / 2
if (newCapacity - minCapacity < 0)//1.5倍扩容之后仍不满足所需容量
newCapacity = minCapacity; //直接使用所需容量值作为数组容量。
if (newCapacity - MAX_ARRAY_SIZE > 0)//满足需求
newCapacity = hugeCapacity(minCapacity);//检查所需容量是否正常,并判断需求容量是否过大,限制最大容量为 MAX_ARRAY_SIZE
//扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
//检查所需容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity>MAX_ARRAY_SIZE)?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
} //private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//public static final int MAX_VALUE = 0x7fffffff;
//MAX_ARRAY_SIZE == 2^31 - 9
addAll()方法
//直接尾插
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();// 将集合C转换成数组
int numNew = a.length;
ensureCapacityInternal(size + numNew); //保证数组内部容量--扩容处理,大小为size + numNew
System.arraycopy(a, 0, elementData, size, numNew); //数组拷贝,插入拷贝值
size += numNew;
return numNew != 0;
}
//指定位置插入
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index); //对添加操作进行索引范围检查,确保在执行添加操作时,所指定的索引处于有效的范围内
Object[] a = c.toArray(); // 将集合C转换成数组
int numNew = a.length;
ensureCapacityInternal(size + numNew);
int numMoved = size - index;//ArrayList容器数组向右移动的位置长度
if (numMoved > 0)
System.arraycopy(elementData, index,
elementData, index + numNew,numMoved);//移位插入
System.arraycopy(a, 0, elementData, index, numNew);//尾插
size += numNew;
return numNew != 0;
}
set()方法
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
remove()方法
//删除指定下标位置元素
public E remove(int index) {
rangeCheck(index);//位置验证
modCount++;
E oldValue = elementData(index);//需要删除的元素
int numMoved = size - index - 1; //左移长度
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved); //数组拷贝并左移
elementData[--size] = null; //置空最后一个元素
return oldValue;
}
//位置验证
private void rangeCheck(int index) {
if (index >= size) //检查下标是否超出
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//删除指定值
public boolean remove(Object o) {
if (o == null) { //因为ArrayList中允许存在null,所以需要判断指定值是否null
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);//移除空值点
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index); //移除等值点
return true;
}
}
return false;
}
//移除指定位置的元素
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null;
}
removeAll()方法
//是继承自AbstractCollection的方法,ArrayList本身并没有提供实现。
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);//检查传入的对象是否为null
return batchRemove(c, false);
}
//检查传入的对象是否为null,为null抛出NullPointerException异常
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
//
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;//获取ArrayList对象的内部数组
int r = 0, w = 0; //初始化两个变量r和w,用于遍历数组和记录有效元素的索引
boolean modified = false; //标记是否有元素被移除
try {
for (; r < size; r++) //遍历内部数组elementData,判断集合c中是否包含当前元素
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];//将当前元素复制到elementData的新索引位置w,并递增w
} finally {
if (r != size) {//判断是否还有未处理的元素
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;//拷贝数组,将剩余未处理的元素复制到新索引位置w开始的位置,并更新w的值
}
if (w != size) { //判断是否有未处理的元素
for (int i = w; i < size; i++)
elementData[i] = null;//将未处理的元素置为null
modCount += size - w;
size = w;
modified = true;
}
}
return modified;//有元素被移除
}
get()方法
ArrayList提供了get(int index)用读取ArrayList中的元素。由于ArrayList是动态数组,所以我们完全可以根据下标来获取ArrayList中的元素,而且速度还比较快,故ArrayList常用于随机访问。
public E get(int index) {
rangeCheck(index);//检查下标是否超出
return elementData(index); //返回下标位置的值
}