下面是对 Java 中 ArrayList 底层核心方法的自我实现,模拟了其动态数组的基本功能,包括添加、删除、查找、扩容等操作:

import java.util.Arrays;

public class MyArrayList<E> {
    // 底层存储元素的数组
    private Object[] elementData;
    // 当前元素数量
    private int size;
    // 默认初始容量
    private static final int DEFAULT_CAPACITY = 10;
    // 空数组(用于初始化)
    private static final Object[] EMPTY_ELEMENTDATA = {};

    // 无参构造器:初始化为空数组,添加第一个元素时扩容至默认容量
    public MyArrayList() {
        this.elementData = EMPTY_ELEMENTDATA;
    }

    // 有参构造器:指定初始容量
    public MyArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("初始容量非法: " + initialCapacity);
        }
    }

    // 获取当前元素数量
    public int size() {
        return size;
    }

    // 判断是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    // 添加元素到末尾
    public boolean add(E e) {
        // 确保容量足够(不足则扩容)
        ensureCapacityInternal(size + 1);
        // 赋值并更新元素数量
        elementData[size++] = e;
        return true;
    }

    // 指定位置添加元素
    public void add(int index, E element) {
        // 检查索引合法性
        rangeCheckForAdd(index);
        // 确保容量足够
        ensureCapacityInternal(size + 1);
        // 从index开始的元素后移一位(复制数组)
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        // 插入新元素
        elementData[index] = element;
        size++;
    }

    // 获取指定位置元素
    @SuppressWarnings("unchecked")
    public E get(int index) {
        rangeCheck(index);
        return (E) elementData[index];
    }

    // 修改指定位置元素
    public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = get(index);
        elementData[index] = element;
        return oldValue;
    }

    // 删除指定位置元素
    public E remove(int index) {
        rangeCheck(index);
        E oldValue = get(index);
        // 计算需要移动的元素数量
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            // 从index+1开始的元素前移一位
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        // 释放最后一个元素的引用(帮助GC)
        elementData[--size] = null;
        return oldValue;
    }

    // 删除指定元素(只删除第一个匹配项)
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++) {
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
            }
        } else {
            for (int index = 0; index < size; index++) {
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
            }
        }
        return false;
    }

    // 快速删除(内部使用,不做索引检查)
    private void fastRemove(int index) {
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        elementData[--size] = null;
    }

    // 清空集合
    public void clear() {
        // 释放所有元素引用
        for (int i = 0; i < size; i++) {
            elementData[i] = null;
        }
        size = 0;
    }

    // 检查索引是否越界(用于get/set/remove)
    private void rangeCheck(int index) {
        if (index >= size) {
            throw new IndexOutOfBoundsException("索引越界: " + index + ", 大小: " + size);
        }
    }

    // 检查添加时的索引是否越界
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0) {
            throw new IndexOutOfBoundsException("索引越界: " + index + ", 大小: " + size);
        }
    }

    // 确保内部容量
    private void ensureCapacityInternal(int minCapacity) {
        // 如果是初始空数组,最小容量取默认容量和所需容量的最大值
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

    // 确保显式容量(判断是否需要扩容)
    private void ensureExplicitCapacity(int minCapacity) {
        // 如果所需容量超过当前数组长度,则扩容
        if (minCapacity - elementData.length > 0) {
            grow(minCapacity);
        }
    }

    // 扩容核心方法
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        // 扩容为原来的1.5倍(位运算效率更高)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果扩容后的容量仍不足,则直接使用所需容量
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        // 复制原数组元素到新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    // 重写toString,方便打印
    @Override
    public String toString() {
        if (size == 0) {
            return "[]";
        }
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < size; i++) {
            sb.append(elementData[i]);
            if (i != size - 1) {
                sb.append(", ");
            }
        }
        sb.append("]");
        return sb.toString();
    }
}

核心实现说明:

  1. 底层结构:使用 Object[] 数组存储元素,支持泛型(通过类型擦除实现)。

  2. 容量管理

    • 初始容量默认为 10(无参构造器),也可指定初始容量。
    • 当元素数量达到当前容量时,触发扩容,扩容后的容量为原容量的 1.5 倍(通过 oldCapacity + (oldCapacity >> 1) 计算)。
    • 若扩容后的容量仍不足(如初始容量为 0 时添加大量元素),则直接使用所需的最小容量。
  3. 核心方法

    • add(E e):添加元素到末尾,需先检查容量。
    • add(int index, E element):指定位置插入,需移动后续元素。
    • get(int index)/set(int index, E element):获取/修改指定位置元素,需检查索引合法性。
    • remove(int index)/remove(Object o):删除元素,需移动后续元素并释放引用。
  4. 索引检查:通过 rangeCheck 确保操作的索引在有效范围内,避免数组越界。

  5. 内存优化:删除元素后主动释放引用(置为 null),帮助垃圾回收(GC)。

使用示例:

public class TestMyArrayList {
    public static void main(String[] args) {
        MyArrayList<String> list = new MyArrayList<>();
        list.add("A");
        list.add("B");
        list.add(1, "C"); // 在索引1插入"C"
        System.out.println(list); // 输出: [A, C, B]

        list.set(2, "D");
        System.out.println(list.get(2)); // 输出: D

        list.remove(0);
        System.out.println(list); // 输出: [C, D]

        list.remove("D");
        System.out.println(list.size()); // 输出: 1
    }
}

该实现模拟了 JDK 中 ArrayList 的核心逻辑,但简化了部分细节(如序列化支持、迭代器实现等),适合理解动态数组的底层原理。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐