Java基础一:ArrayList

53 阅读5分钟

ArrayList 是 Java 集合框架(Java Collections Framework)中的一个重要类, 它实现了 List 接口,是基于数组实现的动态数组。 由于其内部采用数组来存储元素,因此它可以提供随机访问(即直接通过索引访问元素)的能力, 同时支持自动扩容以容纳更多的元素。下面我将简要地解析 ArrayList 的关键源码部分。

1. 成员变量

ArrayList 的主要成员变量包括:

  • Object[] elementData:用于存储元素的数组。
  • private int size:当前数组中存储的元素数量。
  • private static final int DEFAULT_CAPACITY = 10:默认的初始容量。
  • private static final Object[] EMPTY_ELEMENTDATA = {}:一个空数组实例,用于空实例的共享。
  • private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8:数组的最大容量。

2. 构造函数

ArrayList 提供了几个构造函数来创建实例:

  • 无参构造函数:创建一个空的 ArrayList,其初始容量为 10。
  • 带有初始容量的构造函数:创建一个空的 ArrayList,但你可以指定初始容量。
  • 带有 Collection 的构造函数:通过传入一个 Collection 来创建一个包含 Collection 所有元素的 ArrayList

3. 动态扩容

当向 ArrayList 添加元素时,如果当前数组大小不足以容纳新元素,ArrayList 会自动扩容。 扩容通常是将数组大小增加到原来的 1.5 倍(确切的增长因子可能会根据 JVM 的实现而有所不同), 但也会受到 MAX_ARRAY_SIZE 的限制。

4. 关键方法

  • add(E e):向列表的末尾添加指定的元素。
  • remove(int index):移除列表中指定位置的元素。
  • get(int index):返回列表中指定位置的元素。
  • size():返回列表中的元素数量。
  • ensureCapacity(int minCapacity):确保列表的容量至少为指定的最小值。
  • trimToSize():将数组的容量调整为列表的当前大小。

5. 示例代码片段

以下是一个 add 方法的简化示例:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 扩容检查
    elementData[size++] = e; // 将元素添加到数组末尾,并更新 size
    return true;
}

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

    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容为原来的 1.5 倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // copy 数组
    elementData = Arrays.copyOf(elementData, newCapacity);
}

以上只是 ArrayList 源码的一部分解析,但它涵盖了 ArrayList 的基本工作原理和关键特性。 ArrayList 是 Java 集合框架中非常基础且常用的类, 理解其实现原理对于深入理解 Java 集合框架非常有帮助。


ArrayList和List在Java集合框架中扮演着不同的角色,它们的底层实现和原理既有相似之处也存在显著差异。以下是对它们底层实现和原理的异同点的详细分析:

共同点

  1. 集合框架的一部分

    • ArrayList和List都是Java集合框架(Java Collections Framework)的一部分。List是一个接口,而ArrayList是List接口的一个具体实现类。
  2. 动态扩展

    • 两者都可以动态地扩展其容量以存储更多的元素。尽管List本身是一个接口,但其实现类(如ArrayList)通常都支持动态扩容。

差异点

  1. 实现方式

    • ArrayList:基于数组实现,是一个动态数组。当元素被添加到ArrayList时,如果其当前的容量不足以容纳新元素,则ArrayList会自动扩容。扩容通常涉及创建一个新的、更大的数组,并将旧数组的元素复制到新数组中。ArrayList的扩容机制确保了它可以按需增长,但也可能导致一定的性能开销,特别是在添加大量元素时。
    • List:作为一个接口,它本身不直接实现任何数据结构。List的底层实现可以因实现类的不同而异。例如,除了ArrayList的数组实现外,还有基于链表实现的LinkedList等。
  2. 性能特点

    • ArrayList:由于基于数组实现,ArrayList在随机访问(即通过索引访问元素)时具有较高的效率(时间复杂度为O(1))。然而,在插入和删除元素时,可能需要移动数组中的其他元素以腾出空间或填补空缺,这可能导致较高的时间复杂度(最坏情况下为O(n))。
    • List:由于List是一个接口,其性能特点取决于具体的实现类。以LinkedList为例,它在插入和删除元素时具有较高的效率(时间复杂度为O(1)或O(n),取决于操作的位置),但在随机访问时效率较低(时间复杂度为O(n))。
  3. 线程安全性

    • ArrayList:是非线程安全的。在多线程环境下,如果不进行同步处理,可能会遇到线程安全问题。
    • List:作为一个接口,其线程安全性也取决于具体的实现类。Java提供了线程安全的List实现,如CopyOnWriteArrayList,但这不是List接口本身的固有属性。
  4. 功能差异

    • ArrayList:提供了基于数组的一系列操作方法,如add(E e)remove(int index)get(int index)等。
    • List:作为接口,它定义了操作有序集合的一系列方法,如boolean add(E e)E remove(int index)E get(int index)等。这些方法的具体实现由实现类提供。

综上所述,ArrayList和List在底层实现和原理上存在显著差异。ArrayList基于数组实现,具有动态扩容、随机访问效率高等特点;而List作为一个接口,其底层实现可以多样,性能特点也取决于具体的实现类。在实际应用中,应根据具体需求选择合适的实现类。