一.ArrayList内部的实现方式
ArrayList内部是通过Object[]实现的。
二.源码分析:
(1).构造方法
public ArrayList() {
array = EmptyArray.OBJECT;
}
public ArrayList(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("capacity < 0: " + capacity);
}
array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}
public ArrayList(Collection<? extends E> collection) {
if (collection == null) {
throw new NullPointerException("collection == null");
}
Object[] a = collection.toArray();
if (a.getClass() != Object[].class) {
Object[] newArray = new Object[a.length];
System.arraycopy(a, 0, newArray, 0, a.length);
a = newArray;
}
array = a;
size = a.length;
}
ArrayList有3个构造方法,
先看第一个,array是一个成员变量,它是Object[]类型的,当我们在new一个空参的ArrayList的时候,系统默认调用了EmptyArray中的OBJECT属性,EmptyArray类具体实现如下:
public final class EmptyArray {
private EmptyArray() {}
public static final boolean[] BOOLEAN = new boolean[0];
public static final byte[] BYTE = new byte[0];
public static final char[] CHAR = new char[0];
public static final double[] DOUBLE = new double[0];
public static final int[] INT = new int[0];
public static final Class<?>[] CLASS = new Class[0];
public static final Object[] OBJECT = new Object[0];
public static final String[] STRING = new String[0];
public static final Throwable[] THROWABLE = new Throwable[0];
public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0];
public static final java.lang.reflect.Type[] TYPE = new java.lang.reflect.Type[0];
public static final java.lang.reflect.TypeVariable[] TYPE_VARIABLE =
new java.lang.reflect.TypeVariable[0];
}
从EmptyArray类中可知,当默认使用ArrayList空参的构造方法的时候,ArrayList内部会new Object[0]的空数组。
再看ArrayList的第二个构造方法,先检验参数的有效性,当传入的参数小于0的时候,会抛出非法参数异常。如果参数合法,成员变量array的赋值,使用的是一个三元运算,当capacity == 0时,系统默认生成一个空数组,当capacity>0时,系统会生成一个长度为capacity的数组。
最后看第三个构造方法,第一步对输入参数的合法性检验,若为空,则抛出空指针异常;第二步将输入集合转换成数组a;第三步判断当前数组a是否是Object[]类型,如果不是,创建一个Object类型的数组进行复制(
复制方法使用System.arraycopy),再赋值给a;第四步将转换的数组a赋值给成员变量array;第五步将数组的长度赋值给成员变量size。
(2).add()方法
/**
* Adds the specified object at the end of this {@code ArrayList}.
*
* @param object
* the object to add.
* @return always true
*/
@Override public boolean add(E object) {
Object[] a = array;
int s = size;
if (s == a.length) {
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
a[s] = object;
size = s + 1;
modCount++;
return true;
}
这里只分析简单的add()方法,第一步将数组,集合长度赋值给局部变量a和s;第二步判断集合的长度是否等于数组的长度,如果等于,需要重新分配数组和重新计算分配内存的空间大小。源码计算内存空间的大小,使用的是一个三元表达式,当集合的长度小于 MIN_CAPACITY_INCREMENT/2 时,系统会分配MIN_CAPACITY_INCREMENT各长度;当集合的长度大于 MIN_CAPACITY_INCREMENT/2 时,系统会分配当前长度的一半(s >>1表示当前长度右移一位,相当于s = s/2)。然后将数组的数据赋值到新的数组中,再更新原来数组。
private static final int MIN_CAPACITY_INCREMENT = 12;
第三步将传入的Object对象添加到数组下标为s处;第四步将当前集合长度加1;第五步modCount自增,它主要是用来记录集合修改的次数(
判断是否出现并发修改异常)。
(3).remove()方法
/**
* Removes the object at the specified location from this list.
*
* @param index
* the index of the object to remove.
* @return the removed object.
* @throws IndexOutOfBoundsException
* when {@code location < 0 || location >= size()}
*/
@Override public E remove(int index) {
/**
* 1.局部变量赋值
*/
Object[] a = array;
int s = size;
/**
* 2.判断删除的下标是否超过了集合的长度,超过抛出越界异常
*/
if (index >= s) {
throwIndexOutOfBoundsException(index, s);
}
/**
* 3.获取下标index的所对应的元素
*/
@SuppressWarnings("unchecked")
E result = (E) a[index];
/**
* 4.将下标index后面的所有元素都向前移动一位
*/
System.arraycopy(a, index + 1, a, index, --s - index);
/**
* 5.集合最后一位设置为空,防止内存泄露,因为删除了一个元素
*/
a[s] = null; // Prevent memory leak
/**
* 6.集合长度重新赋值
*/
size = s;
/**
* 7.记录修改次数
*/
modCount++;
return result;
}
(4).clear()方法
/**
* Removes all elements from this {@code ArrayList}, leaving it empty.
*
* @see #isEmpty
* @see #size
*/
@Override public void clear() {
/**
* 判断集合大小是否不等于0
*/
if (size != 0) {
/**
* 将数组的数据元素填充为null
*/
Arrays.fill(array, 0, size, null);
/**
* 集合长度设置为0
*/
size = 0;
/**
* 记录修改次数
*/
modCount++;
}
}