这几天看了一下ArrayList的源码,下面就把我所学到的和大家分享一下。上代码
在源码中,add(E e)方法,先执行的是ensureCapacityInternal()方法,这个方法是进行扩容判断,如果需要扩容就先进行扩容操作。
/**
* 新增元素操作
*/
public boolean add(E e) {
/** 确定是否需要扩容,如果需要,则进行扩容操作. size表示当前list的大小,size+1就是list所需要的空间大小 */
ensureCapacityInternal(size + 1); // Increments modCount!!
// eg1:size=0,elementData[0]="a1",然后a自增为1
elementData[size++] = e;
return true;
}
ensureCapacityInternal(),先由calculateCapacity方法计算出所需要的最小空间大小,再由ensureExplicitCapacity方法来判断是否需要扩容,计算应该扩容成多大,扩容方法就在ensureExplicitCapacity方法中.
/**
* minCapacity表示list所需要的最小空间,比如当前list里面放了5个数,需要的空间大小就是5,你执行add方法添加一个数,需要的空间就是6了,需要加一
* @param minCapacity
*/
private void ensureCapacityInternal(int minCapacity) {
// eg1:第一次新增元素,calculateCapacity方法返回值为DEFAULT_CAPACITY=10
// 扩容操作就在这个方法里面, elementData就是存放数据的数组,minCapacity就是存放数据所需的最小地址
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity(elementData, minCapacity) 计算出存放元素需要的最小空间
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA为常量 {} ,判断elementData是不是初始状态,也就是数组大小为0的时候,是true的话,表示这是第一次扩容
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 初始状态扩容,第一次扩容默认是变成10,但是如果所需要的最小空间大于10,第一次扩容就不是变成10了。比如你直接调用addAll()方法,传入一个大小大于10的list进去.
return Math.max(DEFAULT_CAPACITY, minCapacity); // eg1:满足if判断,DEFAULT_CAPACITY=10
}
return minCapacity;
}
执行ensureExplicitCapacity方法,判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
// eg1: modCount++后,modCount=1
modCount++; // 并发相关
/** 前面的方法都是在计算需要的最小空间,这一步才是关键,判断需要的最小空间是不是大于当前数组大小,如果是那就得扩容数组,这样才能放的下元素 */
if (minCapacity - elementData.length > 0) { // eg1:10-0=10,满足扩容需求
// eg1:minCapacity=10
grow(minCapacity); // 扩容方法
}
}
grow(minCapacity);扩容方法,计算出数组应该扩容之后的大小
// minCapacity需要的最小空间大小
private void grow(int minCapacity) {
/** 当前数组elementData的长度*/
int oldCapacity = elementData.length; // eg1:oldCapacity=0
/**
* oldCapacity >> 1 相当于除以2(这是右移)
* 000100 >> 1 = 000010
* 000100 << 1 = 001000
*/
/** 新增oldCapacity的一半整数长度作为newCapacity的额外增长长度 */
// 得到新数组长度
int newCapacity = oldCapacity + (oldCapacity >> 1); // eg1:newCapacity=0+(0>>1)=0
/** 如果新的长度newCapacity依然无法满足需要的最小扩容量minCapacity,则新的扩容长度为minCapacity */
if (newCapacity - minCapacity < 0) {
// eg1:newCapacity=10
newCapacity = minCapacity;
}
/** 新的扩容长度newCapacity超出了最大的数组长度MAX_ARRAY_SIZE huge:巨大的 */
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
// 调用Arrays工具类,创建一个指定大小的数组,再把元素放到新数组上
elementData = Arrays.copyOf(elementData, newCapacity);
}
add,ensureCapacityInternal()指定完成之后,直接把元素加入到数组,返回true.
public boolean add(E e) {
/** 确定是否需要扩容,如果需要,则进行扩容操作. size表示当前list的大小,size+1就是list所需要的空间大小 */
ensureCapacityInternal(size + 1); // Increments modCount!!
// eg1:size=0,elementData[0]="a1",然后a自增为1
elementData[size++] = e;
return true;
}
这些步骤,我认为中的来说就是。
- 先确定存放这些元素所需要的最小空间(数组为初始化状态且需要的最小空间小于10,直接将最小空间设为10)
- 判断是否需要扩容,如果需要的最小空间数小于当前数组长度,那就可以放的下这些元素,便不需要扩容
2.1 如果需要扩容: 然后计算出应该将数组扩容成多大,根据大小创建新数组,将元素放到新数组
2.2 不需要扩容,数组不变- 加新元素加入到数组,返回true