关键变量
private static final int DEFAULT_CAPACITY = 10;
默认初始化容量
private static final Object[] EMPTY_ELEMENTDATA = {};
当指定长度为0时,返回的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
当调用无参构造方法,返回的是该数组。刚创建一个ArrayList 时,其内数据量为0。它与EMPTY_ELEMENTDATA的区别就是:该数组是默认返回的,而EMPTY_ELEMENTDATA是在用户指定容量为0时返回。
transient Object[] elementData
存储集合元素的底层实现:真正存放元素的数组,可以理解为他就是ArrayList这个壳下的数组,下文称之为操作数组
private int size;
ArrayList的实际大小
贯穿全类的方法
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
Object src : 原数组
int srcPos : 从元数据的起始位置开始
Object dest : 目标数组
int destPos : 目标数组的开始起始位置
int length : 要copy的数组的长度
// 源数组
byte[] srcBytes = new byte[]{2,4,0,0,0,0,0,10,15,50};
// 目标数组
byte[] destBytes = new byte[5];
//创建一个一维空数组,数组的总长度为 5位,然后将srcBytes源数
//组中 从0位 到 第5位之间的数值 copy 到 destBytes目标数组
//中,在目标数组的第0位开始放置.
System.arrayCopy(srcBytes,0,destBytes ,0,5)
构造函数
1、ArrayList()
public ArrayList() {
//只是简单将空数组赋值给操作数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
2、ArrayList(int initialCapacity)
public ArrayList(int initialCapacity) {
//长度大于0则指定一个initialCapacity长度的数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//如果长度为0,也是赋值了空数组,但是与无参的数组
//不同
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
3、ArrayList(Collection<? extends E> c)
public ArrayList(Collection<? extends E> c) {
//直接通过toArray转化为数组
elementData = c.toArray();
因为size代表的是集合元素数量,所以通过别的集合来构
//造ArrayList时,要给size赋值
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
//如果toArray失败,则通过拷贝的方式初始化操作
//数组
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
//否则为空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
关键方法
增
add(E e)
public boolean add(E e) {
//在每次添加数据之前,都会通过 size+1 去判断加了之后是
//不是需要扩容,是的话先进行扩容,再增加元素
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
add(int index, E element)
public void add(int index, E element) {
//越界判断
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
//一样在新增之前判断是否需要扩容
ensureCapacityInternal(size + 1);
//将index开始的数据 向后移动一位,腾出位置给index插
//插入数据
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
//长度 + 1
size++;
}
addAll(Collection<? extends E> c)
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew);
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
ensureCapacityInternal(int minCapacity)
private void ensureCapacityInternal(int minCapacity) {
//这也就是为什么默认构造器的空数组与手动设置的不一样
//将容量设置为minCapacity与初始容量两者的最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//
ensureExplicitCapacity(minCapacity);
}
ensureExplicitCapacity(int minCapacity)
private void ensureExplicitCapacity(int minCapacity) {
//结构改变次数(长度是否变化),现在是add操作,所以+1
//与是否需要扩容没有关系的
modCount++;
//“添加完数据后的最小容量”比当前数组长度还要大,说明添
//加了这条数据后,数组是放不下这么多元素的,需要进行扩
//容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow(int minCapacity)
private void grow(int minCapacity) {
//旧数组的长度
int oldCapacity = elementData.length;
//1、这行很重要!!!
//新数组的长度为:旧数组长度 + 旧数组长度右移 1 位
//因为右移相当于除以2,所以:
//新数组长度为:旧数组 + 旧数组长度的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
//2、这里也很重要!!!
//如果扩容后的长度还是不够
//那就直接将“添加完数据后的最小容量”作为新数组长度
//相当于:不管了,反正下次add也会触发扩容...
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//3、这里也是!!!
//这个方法的作用是:传入一个旧的数组(带元素),传入一
//个新的长度得到一个新数组:长度是新的长度,元素是旧
//的元素
//也就是相当于拉长了数组长度(扩容)
elementData = Arrays.copyOf(elementData, newCapacity);
}
删
remove(int index)
public E remove(int index) {
//越界处理
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
//操作数+1
modCount++;
//取出要删除的那个元素
E oldValue = (E) elementData[index];
//计算出删除后需要移动的元素个数
//例如:size = 10 ,index = 6,那删除后需要移动的元
//素就是:10-6=4,后面四位,但是!size是长度,index是
//下标,所以要算的话得统一算,这里再-1算是将size从长度
//“转成”下标
int numMoved = size - index - 1;
//拷贝数组并覆盖
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将最后一个元素置空
//原注释:clear to let GC do its work:
//空了之后就没有跟gcroot有关联了,就会被检测到
elementData[--size] = null;
return oldValue;
}
remove(Object o)
public boolean remove(Object o) {
//这里并不是判空,而是因为ArrayList可以存储null
//所以这里得if和else都是在找出对应的元素,并执行删除
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;
}
removeAll(Collection<?> c)
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
//两个方法用到batchRemove:
//1、removeAll:删除列表中与c列表元素相同的元素
//2、retainAll:保留列表中与c列表元素相同的元素
return batchRemove(c, false);
}
batchRemove(Collection<?> c, boolean complement)
private boolean batchRemove(Collection<?> c, boolean complement) {
//复制一份数组
final Object[] elementData = this.elementData;
//r是for的循环数,也是elementData的下标
//w是保留下来的数的下标
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
//如果c不包含elementData[0、1、2...]
if (c.contains(elementData[r]) == complement)
//则将这个元素保留下来,并存在以w为递增
//数的下标中
elementData[w++] = elementData[r];
} finally {
//try里面异常了,会导致 r!=size
if (r != size) {
//将出现异常处后面的数据全部复制覆盖到数组里。
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
//置空数组后面的元素
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
改
set(int index, E element)
public E set(int index, E element) {
//因为不涉及修改结构,所以不会修改modCount
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
查
get(int index)
public E get(int index) {
rangeCheck(index);//越界检查
return elementData(index); //下标取数据
}
清空
clear()
public void clear() {
modCount++;
//置空让垃圾回收器检测到
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}