一、创建一个 ArrayList 的对象
ArrayList list = new ArrsyList();
当实例化 ArrayList 时,我们看一下构造器内部都做了什么。
// transient Object[] elementData;
// private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
构造器内部对 elementData 进行了初始化,把它初始化为一个空数组,ArrayList 的元素存放在这个数组里。
二、添加一个元素
ArrayList list = new ArrayList();
... // 可能添加很多个元素了
list.add(new Object());
我们还是看源码
// 将指定的元素追加到集合的末尾
public boolean add(E e) {
// 确保容量是够用的,不够用的话就扩容
ensureCapacityInternal(size + 1); // size: elementData 数组中元素个数,而不是数组的长度
elementData[size++] = e;
return true;
}
接下来我们来看这个扩容的方法
private void ensureCapacityInternal(int minCapacity) {
/*
如果 elementData 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 地址相同,这个最小容量
minCapacity 取 DEFAULT_CAPACITY(默认容量 10) 和 minCapacity 的最大值。
那么什么时候地址值相同呢?
只有第一次添加元素的时候,地址值才会相同,因为当我们实例化 ArrayList 的时候,
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
之后在扩容的时候这个 elementData 地址值就改变了,我在下面有说在哪里改变的
*/
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 确定到底需不需要扩容
ensureExplicitCapacity(minCapacity);
}
接下来看这个方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 修改次数 + 1
// 如果所需要的最小容量 minCapacity 大于 数组的长度,那就需要扩容了
if (minCapacity - elementData.length > 0)
grow(minCapacity); // 扩容
}
只有走到这里才能进行扩容
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 扩容为原来的 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新的容量还不够用,那就你需要多少我就给你多少
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新的容量比 MAX_ARRAY_SIZE 还大,我们就拿最小容量 minCapacity 和 MAX_ARRAY_SIZE作比较,看下面 hugeCapacity() 这个方法
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 进行数组的复制,扩容这里就完事了。接着分析一下?还是?
// 注意:在 copyOf() 这个方法里,elementData 地址值改变了!!!
elementData = Arrays.copyOf(elementData, newCapacity);
}
这是 hugeCapacity() 这个方法,其实里面就进行了判断
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
// 如果最小容量 minCapacity 大于这个 MAX_ARRAY_SIZE,就直接把 Integer.MAX_VALUE 给返回了,否则就返回 MAX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
还是继续分析一下吧,是怎么进行扩容的
// 刚才分析到这里
// elementData = Arrays.copyOf(elementData, newCapacity);
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
接着往下来
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
// 如果这个数组是 Object[],就根据新的容量直接创建一个 Object[] 返回了,
// 如果不是,就根据新的容量创建那个类型的数组,具体怎么实现的,简单点说,就是 Java 调用了 非 Java 的接口,往下看都调用了哪些方法
// 注意:在这里 elementData 地址值改变了!!!
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
// 这个在熟悉不过了吧,就是一个数组的复制
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
分析一下这个Array.newInstance(newType.getComponentType(), newLength);
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
// 这下到底了吧,都看到 native 了
private static native Object newArray(Class<?> componentType, int length)
throws NegativeArraySizeException;
终于把扩容这里说的差不多了,我们终于可以说说添加元素这个事了
public boolean add(E e) {
ensureCapacityInternal(size + 1);
// 就直接把元素放里面就可以了呗,你还在那里想什么想
elementData[size++] = e;
return true;
}
三、移除一个元素
ArrayList list = new ArrayList();
... // 可能添加很多个元素了
list.add(new Object());
list.remove(int index);
继续带你体验看源码的乐趣!!!
public E remove(int index) {
// 检查这个索引是否是有效的,无效就抛出异常,代码在下面,里面就抛了个异常
rangeCheck(index);
modCount++; // 修改次数 + 1
// 返回 elementData[index] 返回给 oldValue,代码在下面
E oldValue = elementData(index);
// 要向前移动元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
// 把你要移除元素的后面的元素往前移动一位
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 把最后一个元素干掉,同时 size--
elementData[--size] = null;
// 把移除元素的那个值返回,这个是不是比那个 add() 舒服多了 ^_^
return oldValue;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
四、插入一个元素
public void add(int index, E element) {
// 检查你插入的索引是否在 [0, size] 的范围里,不在的话会抛出异常的,rangeCheckForAdd() 代码在下面
rangeCheckForAdd(index);
// 这个操作是不是很似曾相识,在 add() 里面就调用了这个方法,保证容量是够用的,不够用的话会扩容
ensureCapacityInternal(size + 1);
// 将index 后面的元素向后移动一位
System.arraycopy(elementData, index, elementData, index + 1, size - index);
// 现在可以把你要插入的元素放进来了
elementData[index] = element;
size++; // 元素个数 + 1
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}