今天在做算法题目的时候,使用到了ArrayList,在获取size想到了一个问题,在使用add操作的时候,ArrayList的源码是怎么实现的,然后我下载 了jdk1.8的源码。
首先,ArrayList的初始容量是10,无论是什么操作,首先会调用最低容量为10。最终的实现是使用了System.arrayopy方法。
如果执行add操作 ,有两种方法可供选择:
1.add(E e)方法,该方法会在ArrayList的尾部插入元素,首先会调用ensureCapacityInternal方法来检查数组的容量,如果数组为空数组,但是插入的位置大于初始容量,则最小容量则会变为插入位置的容量大小。在修改的时候会将modCount参数增加,该参数记录着数组修改的次数(用于数组的迭代,在下一篇博客在谈到),如果该最小容量比现有把数组的容量要大,则进行扩容操作。扩容操作首先会获取当前长度作为原容量,对原容量移位运算(右移1位 >> 1,其实该操作相当于将原容量除以2)来获取需要扩容的大小。然后判断扩容大小与之前的最小容量来判断,将大的一方作为新容量。然后新容量与数组容量最大值比较,最大值为Integer.MAX_VALUE-8,如果大于该值,则容量为最大容量。最后使用Arrays.copy()方法完成扩容。然后将扩容的的elementData的size++的位置的值赋值为e。
2.add(index, e)方法,该方法对比上面的方法,多了几步操作,首先会判断index的值是否超出范围,如果超出范围则会抛出IndexOutOfBoundsException异常,由于插入了元素 ,所以list长度会增加,同样会使用ensureCapacityInternal检查数组容量(同add(E e)方法)。之后,会使用System.arraycopy来讲index位置及之后的集合元素集体后移1位,具体操作为复制index之后的元素放入该list的index+1开始的位置上,然后将index的位置赋值为需要插入的元素,最后执行size++完成接长度的增加
源码如下
public boolean add(E e) {
//检查容量,是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//将参数插入集合尾部,并且完成size的增加
//题外话size++和++size的区别
//size++是先赋值在运算 ++size先运算在赋值
//所以这里是element[size] = e,size=size+1这样的代码这是合起来写了
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
//判断index是否越界
rangeCheckForAdd(index);
//判断是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//对集合复制操作,将index后面的元素复制到index+1后面 完成元素的后移1位
//空出index位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将元素插入index位置
elementData[index] = element;
//集合长度自增
size++;
}
private void ensureCapacityInternal(int minCapacity) {
//判断最小容量,如果苏组为空则最小容量和默认容量对比,保证容量不小于10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//明确容量,并进行扩容
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//修改次数+1
modCount++;
// overflow-conscious code
//如果最小容量大于当前容量则扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 将原容量移位运算 >> 1(相当于除2) 扩容大小为原容量的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩容后还是小于最小容量,则将容量改为最小容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 新容量大于最大容量(Integer.Max_VALUE - 8),则新容量为最大容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//通过拷贝获取新集合,这里copyof返回的是新集合所以要赋值给原集合
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;
}
如果是remove方法:
remove方法,我讲一下我们最常用的remove(index)方法。
首先肯定是对参数index检查是否越界,然后获取index位置后面存在的list的元素的长度,使用System.arraycopy将index之后的数据往前移动一位,覆盖掉原来index上的数据,然后将末尾数据置为null,并且size-1.最后返回删除的元素。
public E remove(int index) {
//检查下标是否越界
rangeCheck(index);
//修改次数+1,迭代器迭代的时候会检查该参数
modCount++;
//获取原来的元素
E oldValue = elementData(index);
//获取index之后的元素的长度
int numMoved = size - index - 1;
if (numMoved > 0)
//通过方法将index之后的元素往前移动一位,将index上的元素覆盖
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//末尾元素置空,并且size-1
elementData[--size] = null; // clear to let GC do its work
//返回删除的元素
return oldValue;
}