一、使用无参构造器创建ArrayList集合
1. 示例代码
public class ArrayListSource {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList(); //跳转至第一步
for (int i = 0; i < 10; i++) {
arrayList.add(i); //需要进行第一次扩容,跳转至第二步
}
for (int i = 11; i <= 15; i++) {
arrayList.add(i); //需要进行第二次扩容
}
arrayList.add(100); //需要进行第三次扩容
arrayList.add(200);
arrayList.add(300);
}
}
2. 源码分析
第一步:
当使用new ArrayList()创建集合时,会调用ArrayList类的无参构造器,在集合内部存在一个空的elementData数组,代码如下
private static final int DEFAULT_CAPACITY = 10; //默认容量
...
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //默认空数组
...
transient Object[] elementData; //存放Object对象的数组
...
private int size; //集合中所包含的元素,默认为0
...
protected transient int modCount = 0;
...
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //MAX_ARRAY_SIZE = 2147483639
...
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //elementData初始化为{}数组,其中size=0
}
第二步:
程序进入for循环,从i=0开始,执行arrayList.add(i)方法,进入ArrayList类中
public boolean add(E e) { //此时:e=1
ensureCapacityInternal(size + 1); //跳转至第三步
elementData[size++] = e;
return true;
}
第三步:
执行ensureCapacityInternal(size + 1),其中size=0
private void ensureCapacityInternal(int minCapacity) { //此时minCapacity=size+1=1,即给集合中添加1个元素,需要的最小容量是1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); //跳转至第四步
}
第四步:
先执行ensureExplicitCapacity()中的嵌套函数calculateCapacity(elementData, minCapacity)
// elementData = {}
// minCapacity = 1
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
// DEFAULT_CAPACITY = 10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //此if语句成立
return Math.max(DEFAULT_CAPACITY, minCapacity); //返回值为10,退出函数,跳转至第五步,
}
return minCapacity;
}
第五步:
执行ensureExplicitCapacity()函数
// minCapacity = 10
// modCount默认为0,然后自加1
// elementData.length = 0
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0) //此时if语句成立
grow(minCapacity); //跳转至第六步
}
第六步:
执行grow(minCapacity)
// minCapacity = 10
// MAX_ARRAY_SIZE = 2147483639
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //oldCapacity=0
int newCapacity = oldCapacity + (oldCapacity >> 1); //newCapacity=0+0/2=0
if (newCapacity - minCapacity < 0) //此if语句成立
newCapacity = minCapacity; //newCapacity = 10
if (newCapacity - MAX_ARRAY_SIZE > 0) //此if语句不成立
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
//此语句执行后,elementData = {null,null,null,null,null,null,null,null,null,null}
}
第七步:
当程序执行完第六步之后,根据方法调用步骤依次返回,直至第二步的第2条程序语句
public boolean add(E e) {//此时:e=1
ensureCapacityInternal(size + 1);
//通过以上方法,确保集合中可以存放e对象
elementData[size++] = e;//此时size=0,之后自加1;e=1
//执行之后 elementData = {1,null,null,null,null,null,null,null,null,null}
return true;
}
第八步:
在for循环中,不断执行 arrayList.add(i)方法,直到for循环结束,以上步骤介绍了ArrayList第一次默认初始化之后存放元素的步骤和扩容机制,当集合中存放的对象达到容量10时,集合需要再次进行扩容。而接下来的每次扩容的容量=原来容量*1.5,即 0 --> 10 --> 15 --> 22 --> 33…
二、使用带参数的构造器创建ArrayList集合
1. 示例代码
public class ArrayListSource {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList(5); //跳转至第一步
for (int i = 0; i < 50; i++) {
arrayList.add(i);
}
}
}
2. 底层源码
第一步:
public ArrayList(int initialCapacity) { //initialCapacity = 5
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity]; //elementData = {null, null,null,null,null}
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
由上ArrayList带参构造器内容可知:
- 当初始容量显示的设置为0时,与默认无参构造器执行方式相同,都是将elementData设置为空数组。
- 当初始容量大于0时,将创建一个Object类型数组,数组大小等于初始容量。
- 当初始容量小于0时,将抛出异常对象IllegalArgumentException
第二步:
创建完成arrayList集合之后,进入for循环,从i=0开始,执行arrayList.add(i)方法,底层代码如下:
public boolean add(E e) { //e = 0
modCount++; //modCount=0,执行之后自加1
add(e, elementData, size); //跳转至第三步
return true;
}
第三步:
//e = 0
//elementData = {null, null,null,null,null}
//elementData.length = 5
//s = 0
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length) //此时if语句不成立
elementData = grow();
elementData[s] = e; //elementData = {{0, null,null,null,null}
size = s + 1; //size = 1
}
第四步:
程序继续执行for循环,直到size=5时,表示集合中元素已经达到容量上限,程序执行第三步中的if语句,对集合进行扩容
private Object[] grow() {
return grow(size + 1); //size = 6,跳转至第五步
}
第五步:
//elementData.length = 5
//minCapacity = 6
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length; //oldCapacity = 5
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //if语句条件成立
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */); //跳转至第六步
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
第六步:
//oldLength = 5
//minGrowth = 1
//prefGrowth = 2
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// assert oldLength >= 0
// assert minGrowth > 0
int newLength = Math.max(minGrowth, prefGrowth) + oldLength; //newLength = 7
if (newLength - MAX_ARRAY_LENGTH <= 0) { //if条件成立
return newLength; //返回第5步
}
return hugeLength(oldLength, minGrowth);
}
第七步:
程序执行elementData = Arrays.copyOf(elementData, newCapacity);
elementData = {0 , 1 , 2 , 3 , 4 , null , null , null}
程序继续执行for循环,当达到集合的容量上限值,继续进行扩容,扩容后的容量=原容量*1.5