整体架构
是一个数组结构
特点
删除添加允许放空值,会自动扩容
Size、isEmpty、set、add等方法时间复杂度O(1)
非线程安全
增强for循环,改变列表大小会抛异常
初始化方法
无参初始化 : ArrayList()
默认大小时0,第一次添加数据扩容后大小时10
指定大小初始化 : ArrayList(int initialCapacity)
执行数据初始化 : ArrayList(Collection<? extends E> c)
扩容机制
内部判断是否需要扩容,如果需要扩容,扩容后直接赋值
从第1次扩容起,每次扩容后大小为上一次扩容的1.5倍
扩容的最大值不会超过Integer的最大值
扩容的基本原理是通过新建一个符合我们预期容量的新数组,然后把老数组的数据拷贝过去,底层都是通过调用 System.arraycopy 的native方法
关键源码如下:
public boolean add(E e) {
// 确保数组大小是否足够,不够执行扩容, size 为当前数组的大小
ensureCapacityInternal(size + 1); // Increments modCount!!
// 直接赋值,线程不安全的
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 如果初始化数组大小时,有给定初始值,以给定的大小为准,不走 if 逻辑
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 确保容积足够
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// 记录数组被修改
modCount++;
// 如果我们期望的最小容量大于目前数组的长度,那么就扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 扩容,并把现有数据拷贝到新的数组里面去
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// oldCapacity >> 1 是把 oldCapacity 除以 2 的意思
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩容后的值 < 我们的期望值,扩容后的值就等于我们的期望值
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果扩容后的值 > jvm 所能分配的数组的最大值,那么就用 Integer 的最大值
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 通过复制进行扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
时间复杂度
O (1) :添加删除只需要通过数组索引
线程不安
因为ArrayList自身的elementData(数组列表)、size(大小)、modConut(当前数组被修改的版本次数)变量在多线程环境下进行各种操作时,由于没有加锁,变量类型并非时可见(volatile)的,如果多线程同时操作同个对象,可能存在值被覆盖的风险,所以多线程操作Arraylist建议使用Collections.synchronizedList 生成的ArrayList 内部添加、删除等操作都有加锁,源码如下:
public boolean add(E e) {
synchronized (mutex) {// synchronized 是一种轻量锁, mutex 表示一个当前
SynchronizedList
return c.add(e);
}
}
