本文基于jdk1.7进行分析。
ArrayList是一个非线程安全的有序集合类,它实现了List<E>, RandomAccess, Cloneable, Serializable接口,继承了AbstractList,类名上的泛型E代表ArrayList所存储的元素的类型。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
全局变量
ArrayList中元素是存储在对象数组中的,我们对ArrayList的操作都是通过对这个对象数组进行操作来实现的。ArrayList有三个全局变量,elementData对象数组用于存储元素,size用于存储List中当前对象的个数,MAX_ARRAY_SIZE用于存储List集合最大容量。
private transient Object[] elementData;//对象数组,用于存储集合中的对象
private int size;//当前List中对象的个数
private static final int MAX_ARRAY_SIZE = 2147483639;//最大List集合容量
构造方法
ArrayList有三个构造方法
方法一:入参是ArrayList初始化容量,如果容量小于0就抛错,否则实例化一个Object对象数组赋值给全局变量elementData;
方法二:没有入参,调用的是方法一,默认对象数组大小为10;
方法三:传入Collection集合的对象,将传入的集合对象转为数组,然后给size赋值,判断如果传入的对象数组不是Object[],则转为Object[]。
//一:传入初始化集合的size
public ArrayList(int var1) {
if (var1 < 0) {
//如果size<0.抛错
throw new IllegalArgumentException("Illegal Capacity: " + var1);
} else {
//实例化对象数组赋值给全局对象数组elementData
this.elementData = new Object[var1];
}
}
//二:不传入初始size,默认大小为10,调用public ArrayList(int var1)
public ArrayList() {
this(10);
}
//三:传入Collection集合的实例对象
public ArrayList(Collection<? extends E> var1) {
//将传入的集合对象转为对象数组
this.elementData = var1.toArray();
//赋值对象数组的长度
this.size = this.elementData.length;
//如果转化后的数组类型不是Object[],那就赋值转为Object[]
if (this.elementData.getClass() != Object[].class) {
this.elementData = Arrays.copyOf(this.elementData, this.size, Object[].class);
}
}
get方法
get方法首先调用rangeCheck方法检查数组下标是否越界,然后通过直接传入的数组下标来获取elementData对象数组中的元素。
//根据元素的位置下标获取对应的元素
public E get(int var1) {
this.rangeCheck(var1);
return this.elementData(var1);
}
//范围检查,检查是否数组下标越界
private void rangeCheck(int var1) {
if (var1 >= this.size) {
throw new IndexOutOfBoundsException(this.outOfBoundsMsg(var1));
}
}
//返回数组下标报错信息
private String outOfBoundsMsg(int var1) {
return "Index: " + var1 + ", Size: " + this.size;
}
set方法
进行范围检查,根据传入的var1下标获取到对应位置的元素var3,再将入参var2元素赋值到var1位置,最后返回var1位置上的原有元素var3。
public E set(int var1, E var2) {
this.rangeCheck(var1);
Object var3 = this.elementData(var1);
this.elementData[var1] = var2;
return var3;
}
add方法
add有两个重载方法,一个是直接将元素添加在数组最后,另一个是在指定位置上添加元素
//方法一,添加元素至ArrayList最后一位
public boolean add(E var1) {
//给ArrayList添加元素之前,确保容量足够大
this.ensureCapacityInternal(this.size + 1);
//将元素赋予到数组的最后一位
this.elementData[this.size++] = var1;
return true;
}
//方法二,添加元素至ArrayList指定下标位置
public void add(int var1, E var2) {
//检查传入的下标是否超出范围
this.rangeCheckForAdd(var1);
//确保容量是否足够
this.ensureCapacityInternal(this.size + 1);
//将var1下标开始(包含var1下标位置上的元素)之后的元素全部后移一位
System.arraycopy(this.elementData, var1, this.elementData, var1 + 1, this.size - var1);
//将传入的元素var2赋值到var1下标位置
this.elementData[var1] = var2;
//当前容量+1
++this.size;
}
//确保ArrayList容量足够大
private void ensureCapacityInternal(int var1) {
++this.modCount;
//如果var1大于ArrayList数组长度,进行扩容
if (var1 - this.elementData.length > 0) {
this.grow(var1);
}
}
//ArrayList容量扩容
private void grow(int var1) {
//获取当前数组长度var2
int var2 = this.elementData.length;
//将var2右移一位加上本身得到var3,var3=1.5var2
int var3 = var2 + (var2 >> 1);
//如果var3 < var1,则var3=var1
if (var3 - var1 < 0) {
var3 = var1;
}
//如果var3大于ArrayList的最大容量2147483639,则调用hugeCapacity方法得到新的var3
if (var3 - 2147483639 > 0) {
var3 = hugeCapacity(var1);
}
//最后将原数组复制到var3容量的新数组中并赋值给elementData
this.elementData = Arrays.copyOf(this.elementData, var3);
}
//获取ArrayList最大容量
private static int hugeCapacity(int var0) {
if (var0 < 0) {
//如果小于0,内存溢出
throw new OutOfMemoryError();
} else {
//如果容量大于ArrayList所限制的最大容量214783639,则返回2147483647
return var0 > 2147483639 ? 2147483647 : 2147483639;
}
}
//给添加方法用的范围检查方法
private void rangeCheckForAdd(int var1) {
//如果添加位置的下标大于当前ArrayList数组大小或者小于0,抛错
if (var1 > this.size || var1 < 0) {
throw new IndexOutOfBoundsException(this.outOfBoundsMsg(var1));
}
}
remove方法
//方法一,返回被删除的元素
public E remove(int var1) {
//范围检查
this.rangeCheck(var1);
++this.modCount;
//获取要被删除的元素
Object var2 = this.elementData(var1);
//获取删除元素后剩下的数组长度
int var3 = this.size - var1 - 1;
//如果删除后的数组长度不为0,
if (var3 > 0) {
//通过数组复制的方式将要删除的元素覆盖掉
System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var3);
}
//将最后一位设置为null
this.elementData[--this.size] = null;
//返回被删除的元素var2
return var2;
}
//方法二,返回是否删除成功
public boolean remove(Object var1) {
int var2;
//如果传入的元素var1为null
if (var1 == null) {
//循环查找为null的元素,若找到使用fastRemove删除,然后返回true
for(var2 = 0; var2 < this.size; ++var2) {
if (this.elementData[var2] == null) {
this.fastRemove(var2);
return true;
}
}
}
//如果传入的元素不为null
else {
//开始循环查找是否有与var1相同的元素,找到则使用fastRemove删除并且返回true
for(var2 = 0; var2 < this.size; ++var2) {
if (var1.equals(this.elementData[var2])) {
this.fastRemove(var2);
return true;
}
}
}
return false;
}
//被remove方法调用
private void fastRemove(int var1) {
++this.modCount;
//计算出被删除后所剩的数组长度
int var2 = this.size - var1 - 1;
//如果长度大于0
if (var2 > 0) {
//通过数组复制的方式将元素覆盖掉以达到删除的效果
System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var2);
}
//将数组最后的元素位置设置为null
this.elementData[--this.size] = null;
}
size方法
获取当前ArrayList的容量大小,size方法很简单,就是返回当前ArrayList中的size变量。
public int size() {
return this.size;
}
isEmpty方法
判断ArrayList是否为空,通过判断size变量是否为0
public boolean isEmpty() {
return this.size == 0;
}
contains和indexOf方法
contains方法内部调用indexOf方法,若indexOf返回值>=0,则当前ArrayList包含var1元素;
indexOf方法返回的是传入元素的下标
public boolean contains(Object var1) {
return this.indexOf(var1) >= 0;
}
public int indexOf(Object var1) {
int var2;
//如果传入的var1元素为null
if (var1 == null) {
//循环ArrayList,查到为null的元素然后返回其下标
for(var2 = 0; var2 < this.size; ++var2) {
if (this.elementData[var2] == null) {
return var2;
}
}
}
//若传入的var1元素不为null
else {
//循环ArrayList,通过equals比对数组中是否有var1相同的元素,若有则返回其下标
for(var2 = 0; var2 < this.size; ++var2) {
if (var1.equals(this.elementData[var2])) {
return var2;
}
}
}
//如果没找到则返回-1
return -1;
}
lastIndexOf方法
与indexOf类似都是返回元素所在下标的方法,但是lastIndexOf是从ArrayList的尾部开始循环查找
public int lastIndexOf(Object var1) {
int var2;
if (var1 == null) {
for(var2 = this.size - 1; var2 >= 0; --var2) {
if (this.elementData[var2] == null) {
return var2;
}
}
} else {
for(var2 = this.size - 1; var2 >= 0; --var2) {
if (var1.equals(this.elementData[var2])) {
return var2;
}
}
}
return -1;
}
toArray方法
这里的toArray方法从表面上看是将ArrayList集合转化为Array数组,其内部实现是将ArrayList中的元素数组复制一份并返回,这里一开始我有些疑惑,为什么不能直接返回elementData,因为它本身就是一个Object数组,后来想想如果直接返回了,则是返回了ArrayList中数组的引用,会对ArrayList中数组造成影响。
public Object[] toArray() {
return Arrays.copyOf(this.elementData, this.size);
}
这个toArray的重载方法我不太能够理解,因为它在传入数组长度大于等于ArrayList中数组长度时,将ArrayList中数组元素赋值到传入的数组中,然后如果传入的数组大于ArrayList中数组长度,则将传入的var1数组中的size下标的元素赋值为null,这里的
public <T> T[] toArray(T[] var1) {
//如果传入的数组长度小于当前ArrayList中数组长度,则返回整个ArrayList数组
if (var1.length < this.size) {
return (Object[])Arrays.copyOf(this.elementData, this.size, var1.getClass());
}
//若传入的数组长度大于等于当前ArrayList中数组长度
else {
//将ArrayList中数组全部复制到var1数组中
System.arraycopy(this.elementData, 0, var1, 0, this.size);
//如果传入的var1数组长度大于当前ArrayList中数组长度size
if (var1.length > this.size) {
//将var1数组中的size下标的元素设置为null,这里如果var1的length比size大很多,那么仅仅将size位置上的元素赋值为null又有什么意义呢,size位置后面的元素还是没有变动,这里有点奇怪
var1[this.size] = null;
}
return var1;
}
}
clear方法
ArrayList的清空方法,循环ArrayList内置数组设值为null,将size赋值为0
public void clear() {
++this.modCount;
for(int var1 = 0; var1 < this.size; ++var1) {
this.elementData[var1] = null;
}
this.size = 0;
}
removeAll和retainAll方法 TODO
removeAll和retainAll调用了同一个私有方法,但是入参不同,removeAll是用于删除ArrayList中所有元素的方法,retainAll方法是用于判断集合之间是否有交集的方法。
public boolean removeAll(Collection<?> var1) {
return this.batchRemove(var1, false);
}
public boolean retainAll(Collection<?> var1) {
return this.batchRemove(var1, true);
}
//字面意思是批量删除
private boolean batchRemove(Collection<?> var1, boolean var2) {
//将var3赋值ArrayList的数组elementData
Object[] var3 = this.elementData;
int var4 = 0;
int var5 = 0;
boolean var6 = false;
while(true) {
boolean var11 = false;
try {
var11 = true;
if (var4 >= this.size) {
var11 = false;
break;
}
if (var1.contains(var3[var4]) == var2) {
var3[var5++] = var3[var4];
}
++var4;
} finally {
if (var11) {
if (var4 != this.size) {
System.arraycopy(var3, var4, var3, var5, this.size - var4);
var5 += this.size - var4;
}
if (var5 != this.size) {
for(int var9 = var5; var9 < this.size; ++var9) {
var3[var9] = null;
}
this.modCount += this.size - var5;
this.size = var5;
var6 = true;
}
}
}
}
if (var4 != this.size) {
System.arraycopy(var3, var4, var3, var5, this.size - var4);
var5 += this.size - var4;
}
if (var5 != this.size) {
for(int var7 = var5; var7 < this.size; ++var7) {
var3[var7] = null;
}
this.modCount += this.size - var5;
this.size = var5;
var6 = true;
}
return var6;
}