首页 新能源汽车

手撸 List:从零开始模拟实现经典数据结构

字数: (0926)
阅读: (9056)
内容摘要:手撸 List:从零开始模拟实现经典数据结构,

在日常开发中,List 作为一种常用的数据结构,几乎是每个开发者都要频繁使用的工具。但你是否深入思考过 List 底层的实现原理?本文将带你从零开始,模拟实现 List,揭开它神秘的面纱。通过本文的学习,你不仅能够掌握 List 的基本概念,还能了解到在实际应用中可能遇到的问题和解决方法。在面对诸如 ArrayList 的动态扩容,LinkedList 的内存分配等问题时,更加得心应手。尤其是在高并发场景下,理解这些底层原理有助于编写更高效、更稳定的代码,例如在 Nginx 中,对于连接请求的处理,就涉及到了类似 List 的数据结构管理。

List 的核心概念与接口设计

List 是一种线性表数据结构,它允许我们存储一组有序的元素。List 的主要特点是可以按照索引访问元素,并且可以动态地添加、删除元素。List 的接口设计通常包含以下核心方法:

手撸 List:从零开始模拟实现经典数据结构
  • add(element): 向 List 的末尾添加一个元素。
  • add(index, element): 在 List 的指定位置插入一个元素。
  • remove(index): 移除 List 中指定位置的元素。
  • get(index): 获取 List 中指定位置的元素。
  • set(index, element): 设置 List 中指定位置的元素。
  • size(): 返回 List 中元素的个数。
  • isEmpty(): 判断 List 是否为空。

在 Java 中,ArrayList 和 LinkedList 是 List 接口的两种常见实现。ArrayList 基于数组实现,具有快速随机访问的优点,但插入和删除元素的效率较低;LinkedList 基于链表实现,插入和删除元素的效率较高,但随机访问的效率较低。在选择 List 的实现时,需要根据具体的应用场景进行权衡,类似于在 Nginx 配置中选择合适的负载均衡策略,需要考虑到并发连接数、服务器性能等因素。

手撸 List:从零开始模拟实现经典数据结构

基于数组的 List 模拟实现

下面我们使用 Java 语言来模拟实现 List,首先,我们创建一个名为 ArrayListImpl 的类,并定义一个数组来存储 List 中的元素。

手撸 List:从零开始模拟实现经典数据结构
public class ArrayListImpl<T> {
    private Object[] data; // 存储元素的数组
    private int size;      // List 中元素的个数
    private int capacity;  // 数组的容量

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

    public ArrayListImpl() {
        this(DEFAULT_CAPACITY); // 调用带参构造函数
    }

    public ArrayListImpl(int capacity) {
        this.capacity = capacity;
        this.data = new Object[capacity]; // 初始化数组
        this.size = 0; // 初始元素数量为 0
    }

    // 添加元素到末尾
    public void add(T element) {
        ensureCapacity(); // 确保数组容量足够
        data[size++] = element; // 添加元素,并更新 size
    }

    // 添加元素到指定位置
    public void add(int index, T element) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
        ensureCapacity(); // 确保数组容量足够
        System.arraycopy(data, index, data, index + 1, size - index); // 将 index 及其之后的元素后移
        data[index] = element; // 插入元素
        size++; // 更新 size
    }


    // 移除指定位置的元素
    public T remove(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
        T removedElement = (T) data[index]; // 记录被移除的元素
        System.arraycopy(data, index + 1, data, index, size - index - 1); // 将 index 之后的元素前移
        data[--size] = null; // 将最后一个元素置为 null,方便 GC
        return removedElement; // 返回被移除的元素
    }

    // 获取指定位置的元素
    public T get(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
        return (T) data[index]; // 返回元素
    }

    // 设置指定位置的元素
    public void set(int index, T element) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
        data[index] = element; // 设置元素
    }

    // 返回 List 中元素的个数
    public int size() {
        return size; // 返回 size
    }

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

    // 确保数组容量足够
    private void ensureCapacity() {
        if (size == capacity) {
            int newCapacity = capacity + (capacity >> 1); // 扩容 1.5 倍
            Object[] newData = new Object[newCapacity]; // 创建新数组
            System.arraycopy(data, 0, newData, 0, size); // 将原数组中的元素复制到新数组中
            data = newData; // 更新 data 指向新数组
            capacity = newCapacity; // 更新 capacity
        }
    }
}

在上面的代码中,我们定义了一个 data 数组来存储 List 中的元素,size 变量来记录 List 中元素的个数,capacity 变量来记录数组的容量。当 List 中的元素个数达到数组的容量时,我们需要对数组进行扩容。在 ensureCapacity() 方法中,我们将数组的容量扩大为原来的 1.5 倍,并将原数组中的元素复制到新数组中。这种动态扩容的机制,类似于数据库连接池的管理,都需要考虑到性能和资源消耗。

手撸 List:从零开始模拟实现经典数据结构

实战避坑:ArrayList 的扩容机制

在模拟实现 ArrayList 时,扩容机制是一个需要特别注意的点。如果扩容策略不合理,可能会导致频繁的内存分配和复制,从而影响性能。在上面的代码中,我们将数组的容量扩大为原来的 1.5 倍,这是一个常见的扩容策略。但是,在实际应用中,我们需要根据具体的场景选择合适的扩容策略。例如,如果 List 中存储的是大型对象,那么每次扩容都需要复制大量的数据,这时可以考虑使用更大的扩容因子,以减少扩容的次数。此外,我们还需要注意避免过度扩容,即数组的容量远远大于 List 中元素的个数,这会造成内存浪费。另外,频繁的 System.arraycopy 操作也可能成为性能瓶颈,尤其是在高并发场景下,需要考虑使用更高效的复制方法,例如使用 Arrays.copyOf() 方法。在实际项目中,我们可以使用宝塔面板等工具来监控服务器的内存使用情况,以便更好地优化扩容策略。

手撸 List:从零开始模拟实现经典数据结构

转载请注明出处: 代码一只喵

本文的链接地址: http://m.acea2.store/blog/815530.SHTML

本文最后 发布于2026-04-18 22:08:07,已经过了8天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 路过的酱油 2 天前
    代码示例很清晰,可以直接拿来学习和参考。