ArrayList
1、ArratList无参构造器形式初始容量是0,加入第一个数据时容量设置为10,随后每次到达容量都会按当前容量的1.5倍扩容。
2、arraylist指定容量的构造方式创建则初始容量是指定的容量,而后面每次到达容量上限都会按1.5倍扩容。
源码:
在ArrayList中,使用一个Object类型的数组实现对元素进行保存。
首先编写测试代码:
List<Integer> list = new ArrayList<>();
for (int i = 0; i <= 10; i++) {
list.add(i);
}
进入add方法:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
会进入Integer类的valueOf方法进行自动装箱。装箱结束后进入了ArrayList的add方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
重点在ensureCapacityInternal(size + 1);
方法,它会去确定数组的容量是否足够,不够则进行扩容,进入该方法:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
这里minCapacity
参数是size+1
的大小,这里size是数组的元素数量,在第一次调用add方法的时候是0,所以minCapacity
参数的值第一次是1,即最少需要1个空间的大小才能装得下。可以看到这个方法调用了ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
方法再次对容量进行确认。其中,
calculateCapacity(elementData, minCapacity))
方法对需要扩容的最小容量进行了确认,实际是针对第一次扩容,进入calculateCapacity(elementData, minCapacity))
方法:
elementData是存储数据的Object数组,而`calculateCapacity(elementData, minCapacity)`方法对
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
其中,DEFAULTCAPACITY_EMPTY_ELEMENTDATA在类中定义如下:
此时存在两种情况:1、如果此时存储数据的elementData数组是空数组,则会通过 Math.max(DEFAULT_CAPACITY, minCapacity);
方法将需要的数组大小minCapacity和默认初始大小10比较,并返回二者中的较大者。2、如果此时不是空数组,即意味数组里有元素,则会直接返回需要的最小容量minCapacity。 这个方法结束后,ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
方法就得到了最少需要的数组容量,并调用该方法,进入ensureExplicitCapacity
方法:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount++;标识对数组的修改次数加一,用以防止多个线程操作的情况。而在if语句中会判断最少需要的容量和当前数组的长度的大小关系,当需要的容量已经大于当前数组的长度,时,就会调用grow方法进行数组的扩容,进入grow方法:
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);
}
这里首先把数组的长度作为旧容量存放,然后新的容量是旧容量的1.5倍,右移一位就是减半操作。随后将新容量和需要的容量作比较进行减法操作,如果结果小于0即新容量小于需要的容量,就会把需要的容量赋值给新容量,这个情况对应着第一次操作时oldCapacity的值是0的情况,此时0的1.5倍还是0。
第二个if判断新容量是否超过了最大数组容量,最大量的定义如下:
最后,通过Arrays类的copyOf方法赋值了一个新数组,新数组大小是新容量的值,并且多余的值用null填充,这步结束后跳回最初的方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
把要加入的对象e放在size位置后size后移一位。此时整个操作结束
这里第一次加入元素的扩容和后面的扩容的不同之处在于下面的判断:
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
此时由于elementData不再是一个空数组,所以会直接返回需要的容量。