ArrayList是我们在开发过程中经常使用的一个集合类,所以我们有必要对其了解详细。
常量
DEFAULT_CAPACITY
集合的默认容,创建集合时,如果没有指定容量,在第一个元素
添加进来时,会默认扩展为该容量大小。
private static final int DEFAULT_CAPACITY = 10;
EMPTY_ELEMENTDATA
空数组,创建ArrayList实例时,如果指定容量为0,则默认使用该数组。
private static final Object[] EMPTY_ELEMENTDATA = {};
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
DEFAULTCAPACITY_EMPTY_ELEMENTDATA 也是空数组,创建Arraylist时,如果不指定容量,则默认使用该数组,由于EMPTY_ELEMENTDATA是用来创建指定容量为0的数组 ,为了区分到底使用哪种方式创建的数组,所以加了一个DEFAULTCAPACITY_EMPTY_ELEMENTDATA,之所以要区分的目的是需要确定第一个元素添加进来,数组要扩容的具体长度。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
成员变量
elementData
ArrayList实际存储元素的地方。
transient Object[] elementData
size
ArrayList包含的元素个数。
private int size;
构造方法
ArrayList(int initialCapacity)
Arraylist有两个构造方法,带参数构造方法接受一个初始容量参数initialCapacity
,如果这个初始容量大于0,就创建一个指定容量大小的Object数组,并将其赋值给elementData
,如果初始容量等于0,就将EMPTY_ELEMENTDATA
赋值给elementData
,小于0就抛出异常IllegalArgumentException。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
ArrayList()
无参构造方法,会将DEFAULTCAPACITY_EMPTY_ELEMENTDATA
赋值给elementData
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapacityInternal
方法是来确保内部容量,我们继续往下看这个方法。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
首先来看这一个calculateCapacity
计算容量的方法。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
在上面的内容我们解释过,如果创建ArrayList的时候没有指定容量,则会将DEFAULTCAPACITY_EMPTY_ELEMENTDATA
赋值给elementData
,因此,这一行代码的逻辑实际上就是判断集合创建时有没有指定容量。如果没有指定容量,就会返回Math.max(DEFAULT_CAPACITY, minCapacity);
也就是10和size+1的最大值。
然后我们再来看grow(minCapacity)
,这个方法其实就是扩容的具体逻辑。
private void grow(int minCapacity) {
// 将数组已有长度赋值给oldCapacity
int oldCapacity = elementData.length;
//计算新容量,新容量等于老容量的长度+老容量长度的一半,也就是扩容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果新容量小于指定的容量minCapacity,将minCapacity赋值给新容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果新容量大于了最大数组长度,最大数组长度等于Integer最大值-8,之所以减8的原因是防止因为不同的虚拟机导致分配内存失败,因为有些虚拟机会在数组中保留一些标题字
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
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);
}
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.5倍
。
问题思考与回顾
- 创建集合时的默认容量为多少?
- 第一次往集合添加元素时,集合会扩容到多少?
- 集合扩容的策略是怎样的?
- 集合的最大长度是多少?
- 为什么要定义两个长度为0静态常量数组?
这里还有另外一个重要的变量modCount
,它是从父类继承而来,下一期文章将会注重介绍这一变量的作用。