今儿咱来看看ArrayList是怎么扩容的,底层是什么样的
先说结论
1.利用空参构造创建集合时,在底层创建一个默认长度为0的数组。
2.添加第一个元素时,底层会创建一个新的长度为10的数组,要是存不下,就创建一个能正好存下的数组。
3.这个数组存满时,会扩容1.5倍创建新数组,并把旧数组拷贝到新数组中。
4.如果一次添加多个元素,1.5倍还放不下,则新创建的数组的长度以实际为准,并把旧数组拷贝到新数组中。
首先咱先看看空参构造
ArrayList al = new ArrayList<>();
//底层源码
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
elementData是集合中存储对象的数组
DEFAULTCAPACITY_EMPTY_ELEMENTDATA这玩意是一个对象的空数组
然后往里面添加元素
al.add(5);
//底层源码
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {//size+1当作最小容量
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;
}
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;
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);
}
public static T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
这是全部的底层代码,我们分开一点一点看
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
size是记录集合中元素个数的成员变量
调用了ensureCapacityInternal方法,传入size+1,
让下一个数组元素等于添加的对象,返回添加成功
private void ensureCapacityInternal(int minCapacity) {//size+1当作最小容量
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity:用在计算数组的容量,它的参数是一个对象数组和一个最小需要的容量。
ensureExplicitCapacity:用在数组容量不足时扩充数组的大小,它的参数是最小需要的容量。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private static final int DEFAULT_CAPACITY = 10;
DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个常量数组是在ArrayList类中定义的,用于表示一个空的ArrayList。
如果是空的,就返回默认容量(就是10)和最小需要的容量中较大的一个,即Math.max(DEFAULT_CAPACITY, minCapacity)。
人话就是如果集合为空,那就看看所需最小容量和默认容量哪个大,如果默认大就返回10,开辟一个长度为10的数组,如果所需最小容量大,就返回所需最小容量,开辟一个长度为所需最小容量的数组。
那为什么会出现所需最小容量大于默认容量呢?
答案是因为有一个addAll方法可以批量添加数据。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount++,如果所需最小长度大于这个它本身的长度,那就进行扩容。
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);
}
首先计算新的容量,为旧的容量加上一半,即oldCapacity + (oldCapacity >> 1)。这里使用了右移运算符>>,相当于除以2。
然后判断新的容量是否大于等于最小需要的容量,如果不是,就把新的容量设为最小需要的容量。
接着判断新的容量是否超过了数组的最大限制,即MAX_ARRAY_SIZE。如果是,就调用一个叫hugeCapacity的方法,来返回一个合适的容量。
最后,使用Arrays.copyOf方法,把原来的数组复制到一个新的数组中,并把新的数组赋值给elementData。
总结一下吧,ArrayList扩容底层大概分为四步:
1.利用空参构造创建集合时,在底层创建一个默认长度为0的数组。
2.添加第一个元素时,底层会创建一个新的长度为10的数组,要是存不下,就创建一个能正好存下的数组。
3.这个数组存满时,会扩容1.5倍创建新数组,并把旧数组拷贝到新数组中。
4.如果一次添加多个元素,1.5倍还放不下,则新创建的数组的长度以实际为准,并把旧数组拷贝到新数组中。