在ArrayList中,起始化方式有两种:
1.调用无参的构造方法:
public ArrayList() {
//无参构造方法
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
//将ArrayList起始化为一个名叫DEFAULTCAPACITY_EMPTY_ELEMENTDATA的对象
}
↓ 我们找到DEFAULTCAPACITY_EMPTY_ELEMENTDATA
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
// 其实就是创建了一个名为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的Object[]
// 注意:为内容为{},可以理解为长度为0,但不是NUll;
2.调用有参的构造方法:
public ArrayList(int initialCapacity) {
//有参构造方法
if (initialCapacity > 0) {
//如果传入的参数大于0
this.elementData = new Object[initialCapacity];
//按照传入的参数起始化创建一个相应长度的Object[]
} else if (initialCapacity == 0) {
//如果传入的参数等于0
this.elementData = EMPTY_ELEMENTDATA;
//起始化创建一个长度为0的Object[],等同与无参构造
} else {
//如果传入的参数等于0
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
//抛一个名为非法参数异常
}
}
在明白了构造之后,我们来看看扩容。
首先扩容机制会在调用add方法添加元素时才会触发,我们阅读源码,会发现,扩容机制分为下列阶段:
// 添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1);
// 按照元素个数+1,确认数组容量是否够用
elementData[size++] = e;
//将数组第size位置添加为该元素
return true;
}
看完add,我们明白了,要想搞明白是怎么进行扩容的,还得看看ensureCapacityInternal()的内容
二:ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
//这里是调用了ensureExplicitCapacity方法,而里面又调用calculateCapacity方法
}
在这里不要急,我们先进入calculateCapacity()方法阅读一下
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//判断数组是否起始化为{}
return Math.max(DEFAULT_CAPACITY, minCapacity);
//如果是,则返回10
}
return minCapacity;
//返回数组需要的最小容量
}
我们发现在alculateCapacity()方法中返回了一个容量值,其作为参数传入了ensureExplicitCapacity()之中,所以进入了下一步:
三: ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) {
//判断当前容量是否够用
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//判断当前容量是否够用
grow(minCapacity);
//如果不够,进去则进人grow
}
在这里,我们终于来到了最关键的一步,也就是如果容量不够,则需要进入grow()方法进行扩容。
四: grow方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//旧的容量为当前数组长度
int newCapacity = oldCapacity + (oldCapacity >> 1);
//新的容量为当前数组长度的1.5倍
if (newCapacity - minCapacity < 0)
//判断当前的容量是否不足于需要的最小容量
newCapacity = minCapacity;
//如果不足,则新容量为当前需要的最小容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
//判断现在的新容量是否大于int的最大容量-8
newCapacity = hugeCapacity(minCapacity);
//如果大于,则进去hugeCapacity方法返回新的容量
elementData = Arrays.copyOf(elementData, newCapacity);
//按照最新容量复制数组,实现扩容
}
其中这一行代码为扩容的关键:
int newCapacity = oldCapacity + (oldCapacity >> 1);
oldCapacity >> 1表示做向右移一位运算,等同于取二分之一。
所以这一行代码的意义即为:去原数组长度再加上其的一半,总共1.5倍原数组长度为新长度。即数组容量按照1.5倍增长。
然后就是调用copyOf()方法,将旧数组的新容量个元素,copy到一个新数组中。
到这里扩容就完成了。
我们也可以顺带看一下hugeCapacity方法:
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
// 如果当前需要的最小容量小于0
throw new OutOfMemoryError();
//抛出容量超出的错误
return (minCapacity > MAX_ARRAY_SIZE) ?
//返回当前最小容量与MAX_ARRAY_SIZE谁大
Integer.MAX_VALUE :
//最小容量大,返回Integer.MAX_VALUE ,即2147483647
MAX_ARRAY_SIZE;
//否则返回MAX_ARRAY_SIZE,即2147483639
}
在弄明白扩容原理后,我们再回过头看一下两种构造方式,都会如何实现集合元素的添加:
1.如果调用无参的构造方法:
例如
ArrayList<String> list = new ArrayList<String>();
则当第一次添加元素时:
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//这个时候满足了这个条件
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
所以会返回DEFAULT_CAPACITY,即返回10.而10又作为参数,传入了ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
//判断当前容量是否够用
grow(minCapacity);
//如果不够,进去则进人grow
}
此时minCapacity为10,elementData.length为0,所以又会进入grow方法
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
//判断当前的容量是否不足于需要的最小容量
newCapacity = minCapacity;
//如果不足,则新容量为当前需要的最小容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
此时oldCapacity 就为0,newCapacity经过计算也为0,minCapacity还是为10,满足了newCapacity - minCapacity < 0条件,newCapacity = minCapacity,新的容量newCapacity就为10,
elementData = Arrays.copyOf(elementData, newCapacity),
所以扩容后的新数组长度就为10了,第一次扩容就结束了。
当需要传入第11个元素的时候,就会发生第二次扩容,按照我们上面讲解,第二次扩容即为10的1.5倍,即为15,后面的情况以此类推。
2.如果调用有参的构造方法:
例如
ArrayList<String> arrayList = new ArrayList<String>(100);
有了上面的讲解,这里就非常简单了:
当需要传入第101个元素的时候,就会发生扩容,按照我们上面讲解,扩容即为100的1.5倍,即为150,后面的情况以此类推