用Java语言实现一个自定义的ArrayList类

简介: 自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。

以下是一个自定义的 MyArrayList 类实现,模拟了 Java 中 ArrayList 的核心功能,包括动态扩容、元素增删改查等操作,代码附带详细注释:

import java.util.Arrays;

/**
 * 自定义ArrayList实现,模拟动态数组功能
 * 支持泛型,实现基本的增删改查及扩容机制
 */
public class MyArrayList<E> {
   
    // 底层存储元素的数组(泛型擦除后为Object[])
    private Object[] elementData;
    // 当前元素数量(实际存储的元素个数)
    private int size;
    // 默认初始容量(无参构造器首次扩容时使用)
    private static final int DEFAULT_CAPACITY = 10;
    // 空数组(用于初始化为空的情况)
    private static final Object[] EMPTY_ARRAY = {
   };

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

    /**
     * 有参构造器:指定初始容量
     * @param initialCapacity 初始容量(必须≥0)
     * @throws IllegalArgumentException 若初始容量为负数则抛出异常
     */
    public MyArrayList(int initialCapacity) {
   
        if (initialCapacity > 0) {
   
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
   
            this.elementData = EMPTY_ARRAY;
        } else {
   
            throw new IllegalArgumentException("初始容量不能为负数: " + initialCapacity);
        }
    }

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

    /**
     * 判断集合是否为空
     * @return 空返回true,否则返回false
     */
    public boolean isEmpty() {
   
        return size == 0;
    }

    /**
     * 向集合末尾添加元素
     * @param e 要添加的元素
     * @return 始终返回true(模拟ArrayList的add方法)
     */
    public boolean add(E e) {
   
        // 确保容量足够(不够则扩容)
        ensureCapacity(size + 1);
        // 存储元素并更新size
        elementData[size++] = e;
        return true;
    }

    /**
     * 在指定索引位置插入元素
     * @param index 插入位置(必须在0~size之间)
     * @param element 要插入的元素
     * @throws IndexOutOfBoundsException 若索引越界则抛出异常
     */
    public void add(int index, E element) {
   
        // 检查索引合法性(插入时索引可等于size,即末尾追加)
        checkIndexForAdd(index);
        // 确保容量足够
        ensureCapacity(size + 1);
        // 将index及之后的元素向后移动一位(复制数组)
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        // 插入新元素
        elementData[index] = element;
        size++;
    }

    /**
     * 获取指定索引的元素
     * @param index 索引位置
     * @return 该位置的元素
     * @throws IndexOutOfBoundsException 若索引越界则抛出异常
     */
    @SuppressWarnings("unchecked")
    public E get(int index) {
   
        checkIndex(index);
        return (E) elementData[index]; // 泛型强转(编译期unchecked警告)
    }

    /**
     * 修改指定索引的元素
     * @param index 索引位置
     * @param element 新元素
     * @return 被替换的旧元素
     * @throws IndexOutOfBoundsException 若索引越界则抛出异常
     */
    public E set(int index, E element) {
   
        checkIndex(index);
        E oldElement = get(index);
        elementData[index] = element;
        return oldElement;
    }

    /**
     * 删除指定索引的元素
     * @param index 索引位置
     * @return 被删除的元素
     * @throws IndexOutOfBoundsException 若索引越界则抛出异常
     */
    public E remove(int index) {
   
        checkIndex(index);
        E oldElement = get(index);
        // 计算需要移动的元素数量(index之后的元素个数)
        int moveCount = size - index - 1;
        if (moveCount > 0) {
   
            // 将index+1及之后的元素向前移动一位
            System.arraycopy(elementData, index + 1, elementData, index, moveCount);
        }
        // 释放最后一个元素的引用(帮助GC)
        elementData[--size] = null;
        return oldElement;
    }

    /**
     * 删除指定元素(仅删除第一个匹配项)
     * @param o 要删除的元素(可以为null)
     * @return 存在并删除返回true,否则返回false
     */
    public boolean remove(Object o) {
   
        if (o == null) {
   
            // 处理null元素(null只能用==判断)
            for (int i = 0; i < size; i++) {
   
                if (elementData[i] == null) {
   
                    fastRemove(i); // 内部快速删除(不重复检查索引)
                    return true;
                }
            }
        } else {
   
            // 处理非null元素(用equals判断)
            for (int i = 0; i < size; i++) {
   
                if (o.equals(elementData[i])) {
   
                    fastRemove(i);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 清空集合(释放所有元素引用)
     */
    public void clear() {
   
        // 遍历并置空所有元素(帮助GC回收)
        for (int i = 0; i < size; i++) {
   
            elementData[i] = null;
        }
        size = 0; // 重置元素数量
    }

    /**
     * 确保内部容量足够(核心扩容逻辑)
     * @param minCapacity 所需的最小容量
     */
    private void ensureCapacity(int minCapacity) {
   
        // 若当前数组为空(初始状态),则最小容量取默认容量和所需容量的最大值
        if (elementData == EMPTY_ARRAY) {
   
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        // 若所需容量超过当前数组长度,则触发扩容
        if (minCapacity > elementData.length) {
   
            grow(minCapacity);
        }
    }

    /**
     * 扩容核心方法:创建新数组并复制元素
     * @param minCapacity 所需的最小容量
     */
    private void grow(int minCapacity) {
   
        int oldCapacity = elementData.length;
        // 新容量 = 旧容量 + 旧容量/2(即1.5倍扩容,位运算效率更高)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 若1.5倍扩容后仍不足,则直接使用所需最小容量
        if (newCapacity < minCapacity) {
   
            newCapacity = minCapacity;
        }
        // 复制原数组元素到新数组(新数组长度为newCapacity)
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    /**
     * 检查索引是否越界(用于get/set/remove等操作)
     * @param index 要检查的索引
     * @throws IndexOutOfBoundsException 若索引<0或≥size则抛出异常
     */
    private void checkIndex(int index) {
   
        if (index < 0 || index >= size) {
   
            throw new IndexOutOfBoundsException("索引越界:index=" + index + ", size=" + size);
        }
    }

    /**
     * 检查添加操作的索引是否越界(插入时索引可等于size)
     * @param index 要检查的索引
     * @throws IndexOutOfBoundsException 若索引<0或>size则抛出异常
     */
    private void checkIndexForAdd(int index) {
   
        if (index < 0 || index > size) {
   
            throw new IndexOutOfBoundsException("索引越界:index=" + index + ", size=" + size);
        }
    }

    /**
     * 快速删除(内部使用,跳过索引检查以提高效率)
     * @param index 要删除的索引
     */
    private void fastRemove(int index) {
   
        int moveCount = size - index - 1;
        if (moveCount > 0) {
   
            System.arraycopy(elementData, index + 1, elementData, index, moveCount);
        }
        elementData[--size] = null;
    }

    /**
     * 重写toString,方便打印集合内容
     * @return 集合的字符串表示(格式:[e1, e2, ..., en])
     */
    @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) 计算);若 1.5 倍仍不足,则直接使用所需最小容量。
  3. 核心方法

    • 新增:add(E e)(尾插)、add(int index, E element)(指定位置插入,需移动后续元素)。
    • 查询:get(int index)(获取指定位置元素)。
    • 修改:set(int index, E element)(替换指定位置元素)。
    • 删除:remove(int index)(按索引删除)、remove(Object o)(按元素删除,支持 null)。
    • 工具:size()(元素数量)、isEmpty()(是否为空)、clear()(清空集合)。
  4. 异常处理:对越界索引抛出 IndexOutOfBoundsException,对负数初始容量抛出 IllegalArgumentException

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

使用示例

public class TestMyArrayList {
   
    public static void main(String[] args) {
   
        MyArrayList<String> list = new MyArrayList<>();

        // 添加元素
        list.add("Java");
        list.add("Python");
        list.add(1, "C++"); // 在索引1插入元素
        System.out.println(list); // 输出: [Java, C++, Python]

        // 修改元素
        list.set(2, "JavaScript");
        System.out.println(list.get(2)); // 输出: JavaScript

        // 删除元素
        list.remove(0);
        System.out.println(list); // 输出: [C++, JavaScript]

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

        // 清空集合
        list.clear();
        System.out.println(list.isEmpty()); // 输出: true
    }
}

该实现模拟了 JDK ArrayList 的核心逻辑,适合理解动态数组的底层原理。实际开发中建议使用 JDK 自带的 ArrayList(经过高度优化,支持更多功能如迭代器、序列化等)。

目录
相关文章
|
2天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
4天前
|
云安全 数据采集 人工智能
古茗联名引爆全网,阿里云三层防护助力对抗黑产
阿里云三层校验+风险识别,为古茗每一杯奶茶保驾护航!
古茗联名引爆全网,阿里云三层防护助力对抗黑产
|
4天前
|
存储 机器学习/深度学习 人工智能
大模型微调技术:LoRA原理与实践
本文深入解析大语言模型微调中的关键技术——低秩自适应(LoRA)。通过分析全参数微调的计算瓶颈,详细阐述LoRA的数学原理、实现机制和优势特点。文章包含完整的PyTorch实现代码、性能对比实验以及实际应用场景,为开发者提供高效微调大模型的实践指南。
537 2
kde
|
4天前
|
人工智能 关系型数据库 PostgreSQL
n8n Docker 部署手册
n8n是一款开源工作流自动化平台,支持低代码与可编程模式,集成400+服务节点,原生支持AI与API连接,可自托管部署,助力团队构建安全高效的自动化流程。
kde
362 3
|
2天前
|
Linux 虚拟化 iOS开发
VMware Workstation Pro 25H2 for Windows & Linux - 领先的免费桌面虚拟化软件
VMware Workstation Pro 25H2 for Windows & Linux - 领先的免费桌面虚拟化软件
754 4
VMware Workstation Pro 25H2 for Windows & Linux - 领先的免费桌面虚拟化软件
|
3天前
|
JavaScript 开发工具 Android开发
如何在原生 App 中调用 Uniapp 的页面?
如何在原生 App 中调用 Uniapp 的页面?
243 138
|
4天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段四:学术分析 AI 项目 RAG 落地指南:基于 Spring AI 的本地与阿里云知识库实践
本文介绍RAG(检索增强生成)技术,结合Spring AI与本地及云知识库实现学术分析AI应用,利用阿里云Qwen-Plus模型提升回答准确性与可信度。
254 91
AI 超级智能体全栈项目阶段四:学术分析 AI 项目 RAG 落地指南:基于 Spring AI 的本地与阿里云知识库实践