对于源码的学习, 我是这样的, 简单地过一遍, 看一下有那些方法, 重点看经常问到的源码问题.
ArrayList 的基础属性 :
private static final int DEFAULT_CAPACITY = 10;
// 空实例数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 默认大小的空实例数组, 在第一次调用 ensureCapacityInternal 的时候初始化长度为 10
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存放元素的数组
transient Object[] elementData; // non-private to simplify nested class access
// 数组当前的长度
private int size;
// 参数为初始长度的构造方法
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);
}
}
// 不带参数的构造方法, 那么就使用默认大小的空实例数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 参数为 collection 的构造方法
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
从上面的源码可以看到, ArrayList 的底层实现是动态数组, 有三个不同的构造方法.
ArrayList 的常用方法 :
get 方法 :
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
根据下标返回元素, 底层是数组, 所以先需要检查索引是否越界, 然后返回索引对应位置的元素
set 方法 :
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
同样先检查索引是否越界, 然后取出原元素, 将新元素放到该位置
add 方法 :
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
先对判断当前 size + 1 是否需要扩容, 然后进行元素的添加;
如果是在指定位置添加元素, 那么需要将元素位置后面的元素移位, 使用的是 System.arraycopy() 方法
remove 方法 :
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
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;
}
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
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; // clear to let GC do its work
}
remove(int index) : 这里使用了一个 numMoved 变量, 如果要删除的元素为 list 最后的元素, 那么不需要进行移位, 否则就移位, 将最后一位的元素设置为 null.
remove(Object o) : 如果传入的元素为 null, 那么就遍历 list 找到元素为空的地方, 使用 fastRemove 将其删除.
fastRemove (int index) : 和 remove 类似, 删除指定位置的元素.
clear() 方法 :
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
遍历列表将该 list 清空, 所有位置的元素都赋值为 null
扩容
这个应该是重点
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
// 如果当前数组是给定的默认大小空数组实例, 那么就将其最小容量设置为默认容量, 即 10
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 这里就是数组扩容的具体实现
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 新的数组容量为 老容量 + 老容量/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 这个 if 判断是对初始化情况的兼容
// 初始容量小于 10 的扩容就给 10 的大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将原数组拷贝到容量是 newCapacity 的新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
总结 : ArrayList 的底层实现是动态数组, 而在扩容的过程中使用了 Arrays.copyOf() 和 System.arraycopy() 方法来将元素拷贝到新的数组中, 所以我们也需要学习一下这两个方法 :
Arrays.copyof() :
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
这里的实现调用了另外一个三个参数的 copyOf 方法 :
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@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;
}
最后一个参数指定了想要转换的数据的类型, 这里实际上是调用了 System.arraycopy() 方法 :
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
native 说明这是一个调用了 c/c++ 的方法