【Java ArrayList】底层方法的自我实现

【Java ArrayList】底层方法的自我实现

我们来手写一个简易版的 ArrayList,通过自己实现核心方法,来真正理解 ArrayList 的底层原理。

目标是实现以下核心功能(尽量贴近 JDK 的 ArrayList 设计):

  • 动态扩容
  • 添加元素(add)
  • 指定位置添加(add(index, element))
  • 获取元素(get)
  • 删除元素(remove)
  • 修改元素(set)
  • 查询大小(size)
  • 判断是否包含(contains)
  • 查找元素索引(indexOf)

简易版 ArrayList 完整实现

import java.util.Arrays;

/**
 * 简易版 ArrayList 实现(仅供学习理解底层原理)
 */
public class MyArrayList<E> {

    // 默认初始容量
    private static final int DEFAULT_CAPACITY = 10;

    // 空数组实例(用于无参构造时延迟分配)
    private static final Object[] EMPTY_ELEMENTDATA = {};

    // 实际存储元素的数组
    private Object[] elementData;

    // 当前元素个数
    private int size;

    /**
     * 无参构造:默认容量 10,但真正分配在第一次添加元素时
     */
    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("Illegal Capacity: " + initialCapacity);
        }
    }

    /**
     * 添加元素到末尾
     */
    public boolean add(E e) {
        // 检查容量,必要时扩容
        ensureCapacityInternal(size + 1);
        // 放入数组尾部
        elementData[size++] = e;
        return true;
    }

    /**
     * 在指定位置插入元素
     */
    public void add(int index, E element) {
        // 检查索引合法性
        rangeCheckForAdd(index);

        // 确保容量够用(插入后 size+1)
        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];
    }

    /**
     * 修改指定位置元素,返回旧值
     */
    @SuppressWarnings("unchecked")
    public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = (E) elementData[index];
        elementData[index] = element;
        return oldValue;
    }

    /**
     * 删除指定位置元素,返回被删除的元素
     */
    @SuppressWarnings("unchecked")
    public E remove(int index) {
        rangeCheck(index);

        E oldValue = (E) elementData[index];

        // 计算需要移动的元素个数
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            // 向前移动覆盖被删除的位置
            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 boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

    /**
     * 返回元素第一次出现的位置,没找到返回 -1
     */
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i] == null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

    /**
     * 当前元素个数
     */
    public int size() {
        return size;
    }

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

    /**
     * 清空列表
     */
    public void clear() {
        // 帮助 GC
        for (int i = 0; i < size; i++) {
            elementData[i] = null;
        }
        size = 0;
    }

    // ------------------- 内部工具方法 -------------------

    /**
     * 确保容量足够
     */
    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);
        }
    }

    /**
     * 核心扩容逻辑
     * 1.5 倍扩容(JDK 原版策略)
     */
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        // 1.5 倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);

        // 如果 1.5 倍还不够,直接用 minCapacity
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }

        // 防止溢出(理论上很少发生)
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            newCapacity = hugeCapacity(minCapacity);
        }

        // 复制到新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

    private void rangeCheck(int index) {
        if (index >= size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    private String outOfBoundsMsg(int index) {
        return "Index: " + index + ", Size: " + size;
    }

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

    // 测试
    public static void main(String[] args) {
        MyArrayList<String> list = new MyArrayList<>();

        list.add("A");
        list.add("B");
        list.add("C");
        list.add(1, "X");

        System.out.println(list);               // [A, X, B, C]
        System.out.println(list.get(2));        // B
        System.out.println(list.remove(1));     // X
        System.out.println(list);               // [A, B, C]
        System.out.println(list.contains("B")); // true
        System.out.println(list.indexOf("C"));  // 2
    }
}

核心要点总结(面试常问)

  1. 为什么用 Object[] 数组?
    泛型擦除后,底层还是 Object[],通过类型转换使用。
  2. 扩容策略为什么是 1.5 倍?
  • 1.5 倍是时间与空间的折中(2 倍太浪费,1.2 倍扩容太频繁)
  • 使用 oldCapacity + (oldCapacity >> 1) 实现 1.5 倍(位运算更快)
  1. 为什么第一次添加时才分配默认容量 10?
    延迟分配(lazy initialization),节省内存(new ArrayList() 但不 add 时不占空间)
  2. System.arraycopy 为什么快?
    native 方法,底层用 C 实现,批量内存复制效率很高
  3. 删除元素时为什么要把末尾置 null?
    帮助 GC 回收不再引用的对象,避免内存泄漏
  4. 线程安全吗?
    当前版本不安全(多线程 add/remove 会出现数据丢失、越界等)

进阶思考题(建议自己实现或回答)

  • 如何实现线程安全的 ArrayList?(CopyOnWriteArrayList 思路)
  • 如何实现 fail-fast 机制?(modCount)
  • 如何实现 trimToSize() 功能?
  • 如何实现 subList()?
  • 如何实现 toArray() 的泛型版本?

如果你想继续深入某个方法(比如扩容、删除、subList、迭代器等),或者想加上 fail-fast 机制、泛型优化迭代器 等功能,告诉我,我可以继续帮你完善!

文章已创建 4391

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部