public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
ArrayList是java容器中典型的容器,继承于AbstractList,并实现了List接口。
ArrayList是基于数组实现的,默认大小为10,如果ArrayList实例中元素的个数超过了10,便会对容器进行扩容。
private static final int DEFAULT_CAPACITY = 10; //数组默认大小
transient Object[] elementData; //容器中维护的数组
1.构造方法
ArrayList提供了一系列的构造方法,用的最多的:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
eg:ArrayList<> list = new ArrayList<>();
该构造方法将容器中的数组elementData初始化;
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);
}
}
该种构造方法指定了elementData的大小,如果指定容量为0,则初始化elementData为EMPTY_ELEMENTDATA(空),作用是区别于上个构造方法时指定的初始化DEFAULTCAPACITY_EMPTY_ELEMENTDATA(默认空,没有指定的空);
另外一种构造方法将集合类型转化为ArrayList。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
2.动态扩容机制
ArrayList支持动态扩容,当ArrayList实例的size等于了elementData时,就会进行扩容,将elementData的大小扩大到当前容量的1.5倍,做法是新创建一个数组,大小为原数组容量的1.5倍,将原数组的元素copy到新数组中。
public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
该方法用来确定数组大小是否足够(能否满足当前容量),minCapacity表示满足当前容器能接受的最小容量,如果最小容量都大于了数组的长度,那么就有必要进行扩容,即调用grow(minCapacity)。
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
grow方法调用Arrays.copyOf()返回了一个新的elementData,大小为原数组的1.5倍,并且将原数组的元素都复制到新的数组中。
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
其中Arrays.copyOf(elementData,newCapacity(minCapacity))的第二个参数,
newCapacity(minCapacity)返回了新数组的大小。
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //原来的数组大小
int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容之后的数组大小,原数组的1.5倍。
if (newCapacity - minCapacity <= 0) { //扩容后还不够
//针对没有指定容量的初始化时的数组大小的扩容
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0) //正常时的扩容
? newCapacity
: hugeCapacity(minCapacity);
}
其中:MAX_ARRAY_SIZE定义为
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
为了避免有些虚拟机针对大于此值时报的OutOfMemoryError。
/**
* The maximum size of array to allocate (unless necessary).
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
hugeCapacity(minCapacity)则是将最后的8也加进来,即将最大容量增加为Integer能表示的最大值。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE)
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
ArrayList常见的add方法:
public boolean add(E e) {
modCount++; //arraylist变化次数
add(e, elementData, size); //私有add方法
return true;
}
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length) //s为容器目前的大小,如果和当前数组容量相等便扩容
elementData = grow(); //调用grow方法扩容,返回新elementData
elementData[s] = e; //将新add进来的元素加入到数组中
size = s + 1;
}
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray(); //将新元素转化为对象数组
modCount++;
int numNew = a.length; //新元素长度
if (numNew == 0)
return false;
Object[] elementData;
final int s;
//新元素长度大于目前容器剩余长度时,扩容至当前size+新元素长度
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
System.arraycopy(a, 0, elementData, s, numNew);//将加入进来的元素拷贝进elementData中。
size = s + numNew;//数组容量增加。
return true;
}
ArrayList常见的remove方法:
public E remove(int index) {
Objects.checkIndex(index, size); //判断index是否正确
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index); //将被删除元素后面元素依次向前拷贝1格。
return oldValue;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
由此可见,元素的remove操作是很耗费时间的。
总结:
ArrayList基于数组实现,默认容量为10;
不断添加元素使得size等于数组elementData大小时便进行扩容,为原大小的1.5倍;
扩容、remove都会造成较大的开销,
前者会copy元素到新数组中,后者会调整数组中元素的位置;
建议:使用ArrayList时最好指定大小,减少扩容的开销,同时避免过多的remove操作,特别是元素较多时。