ArrayList的底层是一个数组实现,其源码位于java.util.ArrayList。以下是ArrayList的关键源码解析:


1. 定义和初始化:ArrayList使用一个Object类型的数组来存储元素,以及一个size变量来记录当前元素个数。初始容量为10。

private static final int DEFAULT_CAPACITY = 10;
private Object[] elementData;
private int size;

这段代码定义了一些关键的成员变量。

  1. private static final int DEFAULT_CAPACITY = 10;
    这是一个私有的静态常量,表示 ArrayList 的默认初始容量。当没有指定初始容量时,ArrayList 会使用这个默认值。
  2. private Object[] elementData;
    这是一个私有的 Object 类型的数组,用于存储 ArrayList 的元素。ArrayList 的底层数据结构就是这个数组。
  3. private int size;
    这是一个私有的整型变量,表示 ArrayList 当前包含的元素数量。它记录了实际存储在 elementData 数组中的元素个数。

通过这些成员变量,ArrayList 实现了动态数组的功能。当添加元素时,如果当前元素数量超过了 elementData 数组的长度,ArrayList 就会进行扩容操作,创建一个更大的数组,并将元素从旧数组复制到新数组中。扩容后,elementData 数组的长度会增加。

需要注意的是,elementData 数组的长度可能会大于实际存储的元素数量(size 变量的值)。这是为了避免每次添加或删除元素时都进行数组的调整操作,从而提高性能。

这些成员变量是 ArrayList 实现的重要组成部分,它们在 ArrayList 的各种方法中被使用和更新,以实现元素的添加、删除和查询等功能。


2. 扩容操作:当元素个数超过当前容量时,会触发扩容操作。默认情况下,扩容为当前容量的1.5倍。使用ensureCapacityInternal方法来确保容量足够。

这几段代码是 ArrayList 类中与数组扩容相关的方法。让我逐个解释这些方法的作用。

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

ensureCapacityInternal 方法用于确保 ArrayList 内部的容量足够存储指定的最小容量 minCapacity。如果 elementData 数组是默认初始容量的空数组(即没有添加过元素),则会将 minCapacity 与默认容量 DEFAULT_CAPACITY 中的较大值进行比较,并将较大值赋给 minCapacity。然后调用 ensureExplicitCapacity 方法来确保容量足够。

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

ensureExplicitCapacity 方法用于确保 elementData 数组的容量至少能够存储 minCapacity 个元素。首先,modCount++ 用于记录 ArrayList 的结构修改次数,以支持迭代器的快速失败机制。然后,通过比较 minCapacityelementData.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);
}

grow 方法用于实际进行数组的扩容操作。首先,获取当前 elementData 数组的长度 oldCapacity。然后,计算新的容量 newCapacity,通过将 oldCapacity 右移一位并加上 oldCapacity 来实现扩容。这里使用位运算右移一位相当于将 oldCapacity 除以 2,因为右移一位等价于除以 2 的整数除法。

接下来,判断 newCapacity 是否小于 minCapacity,如果是,则将 minCapacity 赋给 newCapacity,以确保扩容后的容量不小于 minCapacity

然后,通过比较 newCapacityMAX_ARRAY_SIZE 的差值,判断是否超过了数组的最大容量限制。如果超过了,则调用 hugeCapacity 方法来获取一个合适的最大容量值。

最后,使用 Arrays.copyOf 方法将 elementData 数组复制到一个新的数组中,并将新数组赋给 elementData,完成扩容操作。

这些方法共同实现了 ArrayList 的动态扩容功能,以确保数组容量能够满足添加元素的需求。


3. 添加元素:使用add方法来添加元素到ArrayList中。先确保容量足够,然后将元素添加到数组末尾,并更新size。

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

这段代码是 ArrayList 类中的 add 方法,用于向 ArrayList 中添加元素。让我逐行解释这段代码的作用。

add 方法的参数是要添加的元素 e,返回值为布尔类型,表示添加操作是否成功。

首先,调用 ensureCapacityInternal 方法来确保 ArrayList 内部的容量足够存储新元素。size + 1 表示当前元素数量加上要添加的一个元素。ensureCapacityInternal 方法会根据需要进行数组扩容操作,以确保容量足够。

然后,将要添加的元素 e 放入 elementData 数组中的下一个位置,即 elementData[size]。注意,这里使用了后缀自增运算符 size++,先将 size 的值赋给 elementData[size],然后将 size 的值加 1。

最后,返回 true,表示添加操作成功。

这段代码实现了向 ArrayList 中添加元素的功能。它首先确保容量足够,然后将元素放入数组中,并更新元素数量。


4. 删除元素:使用remove方法来删除指定位置的元素。通过System.arraycopy方法将删除点之后的元素向前移动,然后更新size。

public E remove(int index) {
    rangeCheck(index);
    modCount++;
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // 清空最后一个位置
    return oldValue;
}

这段代码是 ArrayList 类中的 remove 方法,用于移除指定索引位置的元素。让我逐行解释这段代码的作用。

remove 方法的参数是要移除的元素的索引 index,返回值为被移除的元素。

首先,调用 rangeCheck 方法来检查索引是否越界。如果索引超出了有效范围,会抛出 IndexOutOfBoundsException 异常。

然后,modCount++ 用于记录 ArrayList 的结构修改次数,以支持迭代器的快速失败机制。

接下来,通过调用 elementData(index) 方法获取要移除的元素,并将其赋给 oldValue

然后,计算需要移动的元素个数 numMovedsize - index - 1 表示从要移除的索引位置开始,到最后一个元素之间的元素个数。

如果有需要移动的元素(即 numMoved > 0),则使用 System.arraycopy 方法将后面的元素向前移动,以填补被移除的元素位置。具体来说,将 elementData 数组中从 index+1 开始的 numMoved 个元素复制到 elementData 数组中从 index 开始的位置。

最后,将 elementData 数组的最后一个位置设置为 null,以清空被移除的元素。

最后,返回被移除的元素 oldValue

这段代码实现了从 ArrayList 中移除指定索引位置的元素,并将后续元素向前移动的功能。


5. 获取元素:使用get方法来获取指定位置的元素。直接通过索引访问数组中的元素。

public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}

这段代码是 ArrayList 类中的 get 方法,用于获取指定索引位置的元素。让我逐行解释这段代码的作用。

get 方法的参数是要获取的元素的索引 index,返回值为对应索引位置的元素。

首先,调用 rangeCheck 方法来检查索引是否越界。如果索引超出了有效范围,会抛出 IndexOutOfBoundsException 异常。

然后,通过调用 elementData(index) 方法获取指定索引位置的元素,并将其返回。

这段代码实现了从 ArrayList 中获取指定索引位置的元素的功能。它首先检查索引的有效性,然后直接通过索引访问 elementData 数组获取对应位置的元素,并将其返回。


其他方法:ArrayList还提供了一系列其他方法,如set用于替换指定位置的元素,size用于获取元素个数,isEmpty用于判断是否为空,等等。
以上是ArrayList底层实现和源码的简要分析,希望能帮助理解ArrayList的工作原理。


今夜有点儿凉
40 声望3 粉丝

今夜有点儿凉,乌云遮住了月亮。