ArrayList 扩容机制
- 创建一个ArrayList时,并不分配空间:
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 添加操作:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
- 先调用
ensureCapacityInternal
(确保内部容量函数) ensureCapacityInternal
内部调用calculateCapacity
:- 第一次调用,
elementData == {}
, 得到minCapacity = 10
; - 第二次调用,
elementData = {xx} != {}
, 得到minCapacity = size + 1 = 2
;
- 第一次调用,
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
- 再次调用
ensureExplicitCapacity
,判断是否要扩容- 第一次进入时:
minCapacity - elementData.length
=10 - 0
>0
,进入grow(10)
- 第二次添加时:
minCapacity - elementData.length
=2 - 10
<0
,不进入grow()
- 直到第11次时:
minCapacity - elementData.length
=11 - 10
>0
,进入grow(11)
- 第一次进入时:
protected transient int modCount = 0;
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
-
第一次今入,调用
grow(10)
扩容:-
当add第1个元素时,oldCapacity 为0,经比较后第一个if判断成立,
newCapacity = minCapacity = 10
。但是第二个if判断不会成立,即newCapacity
不比MAX_ARRAY_SIZE
大,则不会进入hugeCapacity
方法。数组容量为10,add
方法中return true
,size
增为1。 -
当add第11个元素进入
grow
方法时,newCapacity为15
,比minCapacity =11
大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity
方法。数组容量扩为15,add
方法中return true
,size
增为11。 -
以此类推······
-
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) { //最小需要容量 minCapacity = 10
// overflow-conscious code
int oldCapacity = elementData.length; // old = 0
int newCapacity = oldCapacity + (oldCapacity >> 1); // new = 1.5 * old
if (newCapacity - minCapacity < 0) // 1 - 10 < 0
newCapacity = minCapacity; // new = 10;
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;
}
- 执行完:
elementData = Arrays.copyOf(elementData, newCapacity)
后返回add ()
return true
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
Arrays.copyOf()
之前grow()
方法中用到的扩容函数:
elementData = Arrays.copyOf(elementData, newCapacity);
toArray()
中也用到了:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
源码:
//Arrays.java
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) {
@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;
}
发现 copyOf()
内部实际调用了 System.arraycopy()
方法
System.arraycopy()
add(int index,E element)
中用到的扩容函数:
System.arraycopy(elementData, index, elementData, index + 1,size - index);
源码:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
两者联系和区别
联系:
看两者源代码可以发现 copyOf() 内部实际调用了 System.arraycopy()
方法
区别:
arraycopy()
需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 copyOf()
是系统自动在内部新建一个数组,并返回该数组。
ensureCapacity()
这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的
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);
}
}
最好在 add 大量元素之前用 ensureCapacity
方法,以减少增量重新分配的次数