ArrayList是我们最常用的链表之一,经常作为动态数组使用,今天我们将对ArrayList源码一探究竟。
1.Class定义
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
AbstractList提供了List接口的骨干实现,是一个抽象类。
List接口定义了链表必须的方法。
RandomAccess是一个标记接口,没有定义任何内容。
Cloneable接口实现了浅拷贝方法。
Serializable是序列化接口,无任何内容,用来标记可序列话。
2.私有字段
ArrayList有两个私有字段:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
elementData字段用来存储List中的数据。size字段存储List的长度信息。其中,transient关键字表示,在序列化时,对此字段不进行序列化操作。
3.构造方法
ArrayList有三个构造方法
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
第一个方法传入了初始容量参数,对elementDatad数组的初始大小进行了设置。
public ArrayList() {
this(10);
}
第二个无参方法将elementData大小默认设置为10.
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
第三个方法传入一个集合实例,将实例转为内置elementData并设置了size值。(若不是Object[]则调用Arrays.copyOf方法将其转为Object[])。
4.add()方法
(1)add(E e)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
查看我们最常用的add方法,方法中第二行很容易理解,将e存入elementData中并将size加一。那么,第一行的ensureCapacityInternal(size+1)方法是做什么的呢,根据字面意思,我们猜测是对数组elementData进行操作的,因素elementData是定长的,遇到长度不够时,一定是要改变的。现在进入该方法来查看。
/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
if (minCapacity > 0)
ensureCapacityInternal(minCapacity);
}
private void ensureCapacityInternal(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
modcount记录了调用ensureCapacityInternal()的次数。接着进行判断,需要的最小长度minCapaty是否大于elementData的长度,如果没有,则不用进行额外操作,如果大于,则需要调用grow函数拓展elementData。
在grow函数中,将新容量定为原elementData长度的两倍,并与minCapacity、最大长度进行计算,得到实际更新的长度newCapacity,最后调用Arrays.copyOf(elementData,newCapacity)来更新elementData。
(2).add(int index,E e)方法
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++;
}
该方法在指定位置插入数据。rangeCheckForAdd()用来检测指针范围。System.arraycopy用来将index后面的元素全部后移一位。最后在插入element,size加一结束。
(3).其他add方法原理类似,不再赘述。
5.claer()方法
public void clear() {
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
对于申请到的数组长度,clear()方法并不准备去改变。只是简单的将elementData全部设为null。
6.clone()方法
public Object clone() {
try {
@SuppressWarnings("unchecked")
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
clone方法调用父类clone方法,再将elementData浅克隆给v,将modcount设为0后返回。
7.contains(Object o)方法
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
contains()方法只是包装了indexOf()方法,下面直接看indexOf方法。
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
简单的遍历加equals()方法。同理,lastIndexOf(Object o)方法类似,只是反向遍历即可。
8.get(int index)方法
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
不说了,一看就懂。
9.remove(Object o)方法
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;
}
通过遍历找到该元素的位置index,然后调用fastRemove()方法,下面进入该方法。
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; // Let gc do its work
}
依然是对数组直接进行覆盖操作,然后将最后一位置null即可。
10.toArray(T[] a) 方法
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
如果a足够大,则将elementData存入a,否则新建一个数组并返回。
11.trimToSize()
将此ArrayList实例容量调整为列表的当前大小。
public void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (size < oldCapacity) {
elementData = Arrays.copyOf(elementData, size);
}
}
将elementData存入新的正合适的数组中。
ArrayList的基本操作还是比较容易理解的。希望对大家有所帮助。