java集合框架之ArrayList源码分析——如何扩展容量
大家好,我是Andy,那个永远积极向上且自恋的Andy
1.介绍
ArrayList底层维护的是一个动态数组,每个ArrayList实例都有一个容量,并随着往ArrayList里面添加元素,其容量也自动增长.
下面通过ArrayList源码分析其原理.
以下代码取自JDK1.8
2.代码分析
注:请阅读代码注释结合代码来看
1.主要变量
transient Object[] elementData; // 真正存储数据的数组,整个ArrayList的元素数据都存在这个Object[]中.
private int size; // 实际ArrayList存储数据的元素大小(非容量).
2.构造函数
//指定ArrayList的大小
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 指定数组大小(上面说了ArrayList是以elementData 存储元素数据的)
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 指定一个空数组,没有任何大小(相当于只是声明了数组变量)
// private static final Object[] EMPTY_ELEMENTDATA = {};
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
// 指定一个空数组,没有任何大小(相当于只是声明了数组变量)
// private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//传入一个集合
public ArrayList(Collection<? extends E> c) {
// 返回包含此 collection 中所有元素的数组
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
// 复制指定的数组,返回包含相同元素和长度的Object类型的数组
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 指定一个空数组,没有任何大小(相当于只是声明了数组变量)
// private static final Object[] EMPTY_ELEMENTDATA = {};
this.elementData = EMPTY_ELEMENTDATA;
}
}
1) ArrayList()
构造一个没有指定大小的空列表(相当于只是声明了数组变量)。
2) ArrayList(int initialCapacity)
initialCapacity不为 0 时构造一个具有指定初始容量的空列表。
如果initialCapacity指定为 0 ,那么还是同ArrayList()一样。
3) ArrayList(Collection c)
(注:因此处代码放入会影响显示,只需知道是最后一个传入Collection的方法即可,见上面的代码块最后一个方法)
构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
3.add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1);
//添加元素数据,为所存在的最大元素+1位置增加元素
elementData[size++] = e;
return true;
}
解说:上面代码块 add 方法调用ensureCapacityInternal(size + 1); 如下代码:
private void ensureCapacityInternal(int minCapacity) {
// 如果数组是为空数组,那么设置当前数组默认容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//默认容量为:private static final int DEFAULT_CAPACITY = 10;
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
解说:上面代码块 ensureCapacityInternal 方法拿到了默认值为10的默认数组空间
然后调用ensureExplicitCapacity(minCapacity); 如下代码:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//假如是刚刚创建List<String> list1 = new ArrayList<String>();
// 当前容量10 - 所存元素大小0
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
解说:上面代码块 ensureCapacityInternal 方法调用grow(minCapacity); 如下代码:
private void grow(int minCapacity) {
// 老容量
int oldCapacity = elementData.length;
// 新容量,算法为老+(老/2),例如:10+(10/2)
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);
}
解说:上面代码块 grow方法为容量扩展方法
//当minCapacity > MAX_ARRAY_SIZE 取Integer最大值,否则取MAX_ARRAY_SIZE,也就是Integer最大值-8
//private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
总结:
1)
创建ArrayList时是没有分配空间的,只是一个空数组,相当于一个声明的变量。
2)
1.调用add方法时,如果没有指定初始容量,那么会给ArrayList初始容量10。
2.在每次调用add方法都会判断是否需要扩充容量,
用法是:用当前元素最大数量+1(size+1)> 容量(elementData.length)
上述说明:如果当前元素已满,容量不足,那么调用扩充容量的方法grow(int minCapacity)
容量规则是:当前容量的1.5倍,直到Integer最大值-8,如果还需扩展则最大为Integer最大值
注:如有不合理之处请指点,莫让我误人子弟呀。