ArrayList
是 Java 中非常常用的一个集合类,它实现了 List
接口,并且底层是基于 动态数组(也叫 可变大小数组)来存储数据的。它提供了对元素的快速随机访问,并且可以动态地增加容量。
ArrayList
类位于 java.util
包中,继承了 AbstractList
并实现了 List
接口。ArrayList
底层维护了一个数组,通过这个数组来存储集合中的元素。其最重要的成员变量是 elementData
,它是一个对象数组,存储了 ArrayList
中的元素。
public class ArrayList
private static final long serialVersionUID = 8683452581122892189L;
// 该数组存储集合中的元素
transient Object[] elementData;
// 当前元素的个数
private int size;
// 默认的初始容量
private static final int DEFAULT_CAPACITY = 10;
// 空数组
private static final Object[] EMPTY_ARRAY = {};
public ArrayList() {
this.elementData = EMPTY_ARRAY;
}
}
}
ArrayList
提供了多个构造函数:
ArrayList
会初始化一个容量为 10 的默认数组。EMPTY_ELEMENTDATA
,而不是分配一个新的对象数组。这可以节省内存开销。实际上,ArrayList
是通过动态扩容来增加容量的,起始容量 0 不会影响后续的正常使用。ArrayList
,避免多次扩容,提高性能。public ArrayList(int initialCapacity) {
// 如果初始容量大于 0
if (initialCapacity > 0) {
// 为 `elementData` 数组分配指定容量的内存空间
this.elementData = new Object[initialCapacity];
}
// 如果初始容量等于 0
else if (initialCapacity == 0) {
// 设置 `elementData` 为一个空的静态常量数组
this.elementData = EMPTY_ELEMENTDATA;
}
// 如果初始容量小于 0,抛出异常
else {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
}
private void add(E e, Object[] elementData, int s)
这个方法是一个私有的辅助方法,主要用来向 ArrayList
添加元素。它接收以下参数:
e
:要添加的元素。elementData
:当前存储元素的数组。s
:当前 ArrayList
中的元素数量。功能:
elementData
已满(即 s == elementData.length
),则通过调用 grow()
方法扩容数组。e
添加到数组的 s
位置。size
,表示当前 ArrayList
的大小,size = s + 1
。这段代码表示了向数组末尾添加元素时的基本操作,如果数组已经满了,会进行扩容。
private void add(E e, Object[] elementData, int s) { if (s == elementData.length) elementData = grow(); elementData[s] = e; size = s + 1; }
public boolean add(E e)
这个方法是 ArrayList
类的公共 add
方法,用于将元素添加到列表的末尾。它的实现很简单,调用了上面提到的私有 add
方法来执行实际的添加操作。
modCount++
:modCount
是 ArrayList
用来跟踪结构修改次数的变量。修改次数会在每次改变 ArrayList
内容时增加,这主要是为了支持 Iterator
的快速失败机制(fail-fast)。如果在迭代过程中修改了集合,Iterator
会抛出 ConcurrentModificationException
。add(e, elementData, size)
:通过调用私有的 add
方法来将元素 e
添加到 elementData
数组中的 size
位置。最终,add
方法返回 true
,表示元素已成功添加。
public boolean add(E e) { modCount++; add(e, elementData, size); return true; }
public void add(int index, E element)
这个方法是 ArrayList
的另一个 add
方法,用于在指定的位置 index
插入元素。它比单纯的添加元素复杂,因为插入元素时可能会影响到现有元素的位置。此方法的实现步骤如下:
rangeCheckForAdd(index)
:检查插入位置是否有效。rangeCheckForAdd
方法会验证 index
是否在有效范围内(0 到 size
)。如果超出范围,会抛出 IndexOutOfBoundsException
异常。modCount++
:修改次数增加。size
和 elementData
数组:
s == elementData.length
),则扩容数组。System.arraycopy()
:这是数组元素的移动操作。System.arraycopy
方法会将 index
位置及之后的元素向后移动一位,为新元素腾出空间。
elementData, index, elementData, index + 1, s - index
表示将数组中从 index
开始的元素移动到右边一个位置(从 index + 1
开始)。elementData[index] = element;
:将新元素 element
放入指定的 index
位置。size = s + 1
:最后更新 size
,表示当前元素数量增加。这段代码来自于 ArrayList
的 grow
方法,用于在 ArrayList
容量不足时扩展其底层数组的容量。
private Object[] grow(int minCapacity) { return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); }
这段代码是 ArrayList
类中的一个方法,用于计算扩容时的新数组容量。它的目的是根据当前数组的容量和所需的最小容量(minCapacity
),计算一个合适的新容量,并确保容量不会过大,避免出现内存溢出问题。
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; // 获取当前数组的容量
int newCapacity = oldCapacity + (oldCapacity >> 1); // 计算新的容量,增加50%
// 如果新的容量小于所需的最小容量,则做进一步的处理
if (newCapacity - minCapacity <= 0) {
// 如果当前数组是空数组并且其容量是默认空数组容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity); // 返回最大值,确保容量不小于默认值
// 如果 minCapacity 小于 0,说明发生了溢出,抛出内存不足异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 否则,返回 minCapacity 确保新容量至少为所需的容量
return minCapacity;
}
// 如果新的容量小于等于最大数组大小,则直接返回新的容量
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity); // 否则调用 hugeCapacity 方法,确保容量不超过最大限制
}
定义时只初始化,添加第一个元素时,创建为10,超出时扩容为1.5倍。
5.数组与集合之间的转换