概述
该集合是我们实际工作中最常见的集合,他不支持线程安全的操作,支持随机访问(在单线程下对指定索引位的元素读取操作的时间为O(1)。
集合的底层实现是一个数组,容器存储元素的个数不能多于数组的当前容量;size(), isEmpty(), get(), set()方法均能在常数时间内完成;add()方法的时间开销跟插入位置有关
自动扩容机制
每当向集合中添加元素时,集合都会去检查添加元素后,当前集合元素的个数是否会超出当前数组的产的高度,如果超出长度,数组将会进行扩容。
数组在进行扩容时,每次扩容大概会增长为原数组容量的1.5倍;这种操作的代价是很高的,因此在实际应用中,我们应该尽量避免数组容量的扩张,能预计要保存元素的数量时,在构造集合时,就指定其容量。或者根据实际需求,通过调用ensureCapacity方法来手动增加容量。
部分重要源码翻译
package com.zyh.list;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
/**
* @BelongsProject:Java
* @Author:zhangyuhang
* @CreateTime:2022-09-14 21:25
* @Description:TODO
* @Version:1.0
*/
public class ArrayList<E> extends AbstractList<E> implements List<E>,RandomAccess, Serializable {
/**
* 该常量表示集合默认的初始化容量
* 在使用默认的构造方法的情况下,只出现在ArrayList第一个确定容量时
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 该常量会在初始化集合时使用,用于将elementData数组初始化为一个空数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 该常量会在集合第一次添加数据时使用
* 在默认情况下用作于集合第一次扩容的判断依据
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* transient 关键字修饰的成员变量不是该对象序列化的一部分
* 该对象主要用于存储集合中各个数据对象的引用
* non-private to simplify nested class access
*/
transient Object[] elementData;
/**
* 记录当前集合的容量
*/
private int size;
/**
* 为集合设置一个指定的初始化容量
* @param initialCapacity 容量
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 初始化一个默认容量为0 的集合
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 参照一个集合进行初始化
* @param c
*/
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == java.util.ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
/**
* 向集合中新增一个对象
* @param e
* @return
*/
public boolean add(E e) {
// Increments modCount!!
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
/**
* 如果集合需要扩容,则执行扩容操作
* @param minCapacity 容量
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 计算集合的容量值
* @param elementData 数组
* @param minCapacity 容量
* @return 容量
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果集合是空集合
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//返回默认容量和传入容量的大值
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//如果不是空集合,直接返回传入的值
return minCapacity;
}
/**
* 如果传入容量大于 数组的长度则扩容
* @param minCapacity 容量
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) {
grow(minCapacity);
}
}
/**
* 数组的最大长度
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 集合扩容方法
* @param minCapacity 容量
*/
private void grow(int minCapacity) {
// 存储元素数组的长度
int oldCapacity = elementData.length;
//新集合的容量为 旧容量+旧容量的一般(右移两位)
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0) {
//如果新容量小于传入的容量,则以传入的容量为新容量
newCapacity = minCapacity;
}
if (newCapacity - MAX_ARRAY_SIZE > 0) {
//如果新容量大于数组的最大长度,重新计算数组的最大长度
newCapacity = hugeCapacity(minCapacity);
}
//将旧数组拷贝进新数组,并指定新数组的长度
elementData = Arrays.copyOf(elementData, newCapacity);
}
/**
* 用于防止扩容计算的容量大于数组的最大容量
* @param minCapacity 容量
* @return 容量
*/
private static int hugeCapacity(int minCapacity) {
// overflow
if (minCapacity < 0)
{
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
/**
* 将元素添加到集合指定索引处,指定索引及后面的元素向后移动一位
* @param index 索引
* @param element 元素
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
// Increments modCount!!
ensureCapacityInternal(size + 1);
/**
* elementData 要复制的源数组
* index 从源数组中复制的起始位置
* elementData 进行数组复制的目标数组
* index + 1 在目标数组中复制的起始位置
* size - index 指定进行复制的长度(指定索引后面的元素)
*/
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* 判断指定索引是否正常
* @param index
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0) {
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
/**
* 指定索引错误提示
* @param index
* @return
*/
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
/**
* 返回列表中指定索引处的元素
*
* @param index
* @return
*/
public E get(int index) {
//验证索引是否正常
rangeCheck(index);
return elementData(index);
}
/**
* 返回数组指定下标处的元素
* @param index
* @return
*/
E elementData(int index) {
return (E) elementData[index];
}
/**
* 判断索引是否大于集合长度,大于则抛异常
* @param index
*/
private void rangeCheck(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
/**
* 替换指定索引处的元素,并返回被替换的元素
* @param index
* @param element
* @return
*/
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
/**
* 移除指定索引处的元素,并返回被移除的元素,指定索引后如果还存在元素,则向前移动
* @param index
* @return
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
//被移除的索引后面剩余的元素个数
int numMoved = size - index - 1;
if (numMoved > 0) {
/**
* elementData 拷贝的源数组
* index+1 从源数组拷贝的起始位置
* elementData 拷贝的目标数组
* index 拷贝到目标数组的起始位置
* numMoved 从源数组拷贝的长度
*/
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
}
// 将最后一个元素置空
elementData[--size] = null;
return oldValue;
}
/**
* 根据指定元素移除
* @param o
* @return
*/
public boolean remove(Object o) {
if (o == 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;
}
/**
* 跳过边界检查且不返回已删除值的私有删除方法
* @param index
*/
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;
}
}