You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

面试题:不使用集合类如何在Java中实现动态数组?

嘿,我完全懂你当时的困惑!一开始听到“固定大小的数组怎么实现动态”确实会懵,但其实核心思路就是手动模拟集合类的扩容逻辑——当原数组存满时,创建一个更大的新数组,把原数组的元素复制过去,再用新数组替代旧的。加上泛型就能让这个动态数组支持任意类型,和ArrayList的底层原理本质上是一样的!

手动实现泛型动态数组的核心思路
  • 底层依赖固定大小的数组,但额外维护一个size变量记录实际存储的元素个数
  • 当元素数量达到当前数组容量时,触发扩容(通常扩容为原容量的2倍,保证操作效率)
  • 用泛型封装,让数组可以存储任意引用类型,同时保证类型安全
完整实现代码
public class GenericDynamicArray<T> {
    // 底层存储元素的核心数组
    private T[] elements;
    // 当前实际存储的元素个数
    private int size;
    // 默认初始容量
    private static final int DEFAULT_CAPACITY = 10;

    // 默认构造:初始化默认容量的数组
    @SuppressWarnings("unchecked")
    public GenericDynamicArray() {
        // Java不允许直接创建泛型数组,这里用Object数组强转(泛型擦除特性导致的折中方案)
        elements = (T[]) new Object[DEFAULT_CAPACITY];
        size = 0;
    }

    // 自定义初始容量的构造方法
    @SuppressWarnings("unchecked")
    public GenericDynamicArray(int initialCapacity) {
        if (initialCapacity <= 0) {
            throw new IllegalArgumentException("初始容量不能小于等于0");
        }
        elements = (T[]) new Object[initialCapacity];
        size = 0;
    }

    // 添加元素到数组末尾
    public void add(T element) {
        // 先检查容量,不够就扩容
        ensureCapacity();
        elements[size++] = element;
    }

    // 获取指定索引的元素
    public T get(int index) {
        checkIndexValidity(index);
        return elements[index];
    }

    // 修改指定索引的元素,并返回旧元素
    public T set(int index, T newElement) {
        checkIndexValidity(index);
        T oldElement = elements[index];
        elements[index] = newElement;
        return oldElement;
    }

    // 删除指定索引的元素,并返回被删除的元素
    public T remove(int index) {
        checkIndexValidity(index);
        T removedElement = elements[index];
        // 将索引后的元素向前移动一位,覆盖被删除的位置
        System.arraycopy(elements, index + 1, elements, index, size - index - 1);
        // 清空最后一个位置的引用,帮助GC回收
        elements[--size] = null;
        return removedElement;
    }

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

    // 获取当前数组的总容量
    public int capacity() {
        return elements.length;
    }

    // 检查索引是否越界
    private void checkIndexValidity(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("索引越界:" + index + ",当前元素总数:" + size);
        }
    }

    // 确保数组有足够容量存储新元素
    private void ensureCapacity() {
        // 当元素个数等于数组容量时,触发扩容
        if (size == elements.length) {
            int newCapacity = elements.length * 2; // 扩容为原容量的2倍
            @SuppressWarnings("unchecked")
            T[] newElements = (T[]) new Object[newCapacity];
            // 高效复制原数组元素到新数组
            System.arraycopy(elements, 0, newElements, 0, size);
            elements = newElements;
        }
    }

    // 测试示例
    public static void main(String[] args) {
        GenericDynamicArray<String> strArray = new GenericDynamicArray<>();
        strArray.add("Java");
        strArray.add("泛型");
        strArray.add("动态数组");

        System.out.println("当前元素个数:" + strArray.size());
        System.out.println("当前数组容量:" + strArray.capacity());
        System.out.println("索引1的元素:" + strArray.get(1));

        strArray.set(1, "Generic");
        System.out.println("修改后索引1的元素:" + strArray.get(1));

        strArray.remove(0);
        System.out.println("删除后元素个数:" + strArray.size());
        System.out.println("删除后索引0的元素:" + strArray.get(0));

        // 测试自动扩容
        for (int i = 0; i < 15; i++) {
            strArray.add("测试元素" + i);
        }
        System.out.println("扩容后的数组容量:" + strArray.capacity());
    }
}
关键细节说明
  1. 泛型的使用:通过GenericDynamicArray<T>定义泛型类,T可以替换为任意引用类型(比如String、Integer),避免了使用Object数组时的强制类型转换,同时保证类型安全。
  2. 数组扩容逻辑:ensureCapacity()是核心,当元素填满当前数组时,创建一个2倍容量的新数组,用System.arraycopy高效复制元素,这也是ArrayList等集合类的底层实现方式。
  3. 索引合法性检查:所有涉及索引的操作都会先检查索引是否在有效范围内,避免数组越界异常。

其实面试官要的就是这个思路——底层还是用固定数组,但通过手动扩容和泛型封装,对外提供动态数组的功能。当时没想到很正常,毕竟平时都是直接用现成的集合,很少去抠底层实现~

内容的提问来源于stack exchange,提问作者Mehak Batra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:43:02