ArrayList是我们工作中最常用之一的集合工具。正所谓工欲善其事必先利其器,阅读ArrayList源码是非常有必要的,学习其中的思想,能够帮助我们发现bug,解决bug,写出更优雅的代码。
类图
从ArrayList的类图上可以看到继承了AbstractList,实现了
- List接口:提供了添加、移除、获得等方法。
- RandomAccess接口:标志类接口,代表可以随机访问。
- Serializable接口:标志类接口,代表可以被序列化。
- Cloneable接口:标志类接口,代表可以被克隆。
属性
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
//数组内元素个数
private int size;
构造方法
- 有参构造(容量):如果参数为0,则给elementData赋值空的Object数组,否则创建一个长度为参数的Object数组并赋值给elementData
- 有参构造(集合):将集合参数转成数组,如果该数组运行类型为ArrayList,直接赋值给elementData,否则调用Arrays.copyOf方法复制成Object数组并赋值给elementData
- 无参构造:给elementData赋值空的Object数组
添加方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
- ensureCapacityInternal(size + 1):检查是否需要需要扩容
- elementData[size++] = e; 尾插法,复杂度o(1)
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
- calculateCapacity:如果数组容量为空,minCapacity小于10的情况下返回10,否则返回minCapacity(size + 1)
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
- 当minCapacity大于数组长度,进行扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
- int newCapacity = oldCapacity + (oldCapacity >> 1);将数组长度变成1.5倍,然后使用Arrays.copyOf方法复制一个长度为原数组1.5倍的数组,并将数据复制到新数组。(底层C++实现)
指定位置添加方法
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++;
}
- 判断插入位置下标是否越界
- 扩容(同上)
- 被插入位置元素及后面的所有元素往后移一位,复杂度o(n)
- 插入元素,size加1
移除指定位置方法
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;
}
- rangeCheck:使用size跟index判断是否越界,是抛异常
- System.arraycopy将index后面的元素向前面挪一位,时间复杂度o(n)
- size减1
移除指定元素方法
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 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
}
- 遍历到指定元素下标 ,然后将下标后面的元素向前面挪一位,时间复杂度o(n)
获取元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
-判断下标是否越界,没有越界则直接通过下标获取元素,复杂度o(1)