以下代码均取自ArrayList源码:
- 无参构造ArrayList后,list长度为0,内部数组长度为0。
// 属性
private int size;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 无参构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// size()方法
public int size() {
return size;
}
- 指定初始长度initialCapacity构造ArrayList后,数组长度和list的size都为指定长度initialCapacity。
// 指定初始容量的构造方法
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);
}
}
- 指定集合c构造ArrayList后,数组长度和list的size都为传入的集合c的size。
// 使用给定集合创建ArrayList的构造方法
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
- 如果是第一次add或addAll,添加数量不足10个时,都扩容为10,addAll数量n大于10个时,所需最小容量minCapacity被指定为n;如果不是第一次add,所需最小容量minCapacity被指定为当前list的size加上add或addAll进去的数量n。
// 属性;默认容量10
private static final int DEFAULT_CAPACITY = 10;
/**
* 计算所需最小容量minCapacity的方法
* 这里的入参minCapacity是从add()和addAll()方法传过来的
* 从add()传过来时,minCapacity = size + 1,size就是list.size()的返回值
* 从addAll()传过来时,minCapacity = size + n,n就是addAll进来的元素个数
* 这个方法仅仅是对于空的ArrayList(指无参构造的ArrayList且)的特殊处理,如果不是空的直接返回了minCapacity
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
- 接下来,检查所需最小容量minCapacity是否超过了当前数组长度,如果超过了,则进行扩容grow(),并且modCount加1。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount是继承自父类AbstractList的一个属性,记录了这个ArrayList被修改的次数,会在迭代器中用到。在遍历ArrayList的同时用不恰当的方式修改其中的元素会抛出ConcurrentModificationException,就是依靠这个属性判断的。那么什么是不恰当的方式呢?
比如,使用增强for循环遍历,并使用ArrayList的remove()方法删除元素,是不恰当的,会抛出异常;而使用Iterator遍历,并使用Iterator提供的remove()方法删除元素,是恰当的,不会抛出异常。
下面给出源码对属性modCount的注释:
The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
This field is used by the iterator and list iterator implementation returned by the {@code iterator} and {@code listIterator} methods. If the value of this field changes unexpectedly, the iterator (or list iterator) will throw a {@code ConcurrentModificationException} in response to the {@code next}, {@code remove}, {@code previous}, {@code set} or {@code add} operations. This provides fail-fast behavior, rather than non-deterministic behavior in the face of concurrent modification during iteration.
Use of this field by subclasses is optional. If a subclass wishes to provide fail-fast iterators (and list iterators), then it merely has to increment this field in its {@code add(int, E)} and {@code remove(int)} methods (and any other methods that it overrides that result in structural modifications to the list). A single call to {@code add(int, E)} or {@code remove(int)} must add no more than one to this field, or the iterators (and list iterators) will throw bogus {@code ConcurrentModificationExceptions}. If an implementation does not wish to provide fail-fast iterators, this field may be ignored.
-
接下来,比较当前容量乘以1.5(如果是小数则采用去尾法取近似值,保留整数部分)和所需最小容量minCapacity的大小关系,新容量newCapacity被定为二者中的较大者。
-
接下来,检查新容量newCapacity是否超过了Integer.MAX_VALUE - 8。
ArrayList是使用数组来存储数据的,Java中数组长度属性length的变量类型是int,因此数组最大长度就是int类型的最大值,减去8是因为部分虚拟机会在数组中保留一些header信息,要为这些header信息留出空间。
-
如果没超过则按照新容量newCapacity进行扩容,如果超过了,再检查所需最小容量minCapacity是否超过了Integer.MAX_VALUE - 8。
-
如果所需最小容量minCapacity也超过了Integer.MAX_VALUE - 8,则扩容为Integer.MAX_VALUE(不再为部分虚拟机预留空间),没超过则扩容为Integer.MAX_VALUE - 8。
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
在新容量newCapacity超过了Integer.MAX_VALUE - 8的情况下,为什么不直接扩容为Integer.MAX_VALUE - 8,而是再拿minCapacity和Integer.MAX_VALUE - 8作比较?
个人理解,这样多比较一步可以增加ArrayList的可用率。极限情况下,如果minCapacity恰好介于Integer.MAX_VALUE - 8和Integer.MAX_VALUE之间,则扩容为Integer.MAX_VALUE,这样可以满足本次add或addAll的需求,也就是可以正常添加元素进去而不报错;如果没有这一步比较,则扩容为Integer.MAX_VALUE - 8,这个容量不能满足本次add或addAll的要求。
- 最终,一个ArrayList的最大长度会停留在Integer.MAX_VALUE,元素超过这个容量就会报OutOfMemoryError。
何时会报OutOfMemoryError?
① 元素数量超过Integer.MAX_VALUE时,必定会报OutOfMemoryError。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
② 元素数量没超过Integer.MAX_VALUE时,如果虚拟机存储header占用的空间加上ArrayList本身存储元素占用的空间超过了Integer.MAX_VALUE也会报OutOfMemoryError。