【动态数组底层结构揭秘】ArrayList / Vector 是怎么扩容的?

186 阅读2分钟

🧠 引言

你知道吗?

  • JS 的数组 push() 为啥能无限加?
  • Python 的 list.append() 为啥不抛出“超出容量”的错误?
  • Java 的 ArrayList、C++ 的 vector 为啥插入效率高?

答案都指向了一个核心机制:动态扩容数组

本篇文章带你深挖“动态数组”的底层实现机制,并用 JS/Python 双语代码手写一个具备自动扩容能力的动态数组结构


🧱 一、什么是动态数组?

动态数组是一种能够自动扩容的顺序存储结构,底层依然是“数组 + 容量控制”。

和普通数组区别:

特性普通数组动态数组
是否能自动扩容❌ 否✅ 是
插入末尾效率高(摊销 O(1))
内存使用紧凑可能有冗余空间

🚀 二、动态扩容核心逻辑

思路:

  1. 初始化一个固定大小的数组
  2. 每次插入检查是否超出容量
  3. 超出则创建一个更大数组(如:原大小的2倍)
  4. 将旧数据复制进去

关键点:扩容是成倍增长 + 拷贝旧值


💻 三、JS 实现动态数组

class DynamicArray {
  constructor(initialSize = 4) {
    this.capacity = initialSize;
    this.length = 0;
    this.data = new Array(this.capacity);
  }

  push(value) {
    if (this.length === this.capacity) {
      this._resize();
    }
    this.data[this.length++] = value;
  }

  _resize() {
    this.capacity *= 2;
    const newData = new Array(this.capacity);
    for (let i = 0; i < this.length; i++) {
      newData[i] = this.data[i];
    }
    this.data = newData;
  }

  get(index) {
    if (index < 0 || index >= this.length) throw new Error("越界访问");
    return this.data[index];
  }

  print() {
    console.log(this.data.slice(0, this.length));
  }
}

// 测试
const arr = new DynamicArray();
arr.push(1);
arr.push(2);
arr.push(3);
arr.push(4);
arr.push(5);
arr.print(); // [1, 2, 3, 4, 5]

🐍 四、Python 实现动态数组(模拟 C 风格)

class DynamicArray:
    def __init__(self, capacity=4):
        self.capacity = capacity
        self.length = 0
        self.data = [None] * self.capacity

    def push(self, value):
        if self.length == self.capacity:
            self._resize()
        self.data[self.length] = value
        self.length += 1

    def _resize(self):
        self.capacity *= 2
        new_data = [None] * self.capacity
        for i in range(self.length):
            new_data[i] = self.data[i]
        self.data = new_data

    def get(self, index):
        if index < 0 or index >= self.length:
            raise IndexError("越界访问")
        return self.data[index]

    def print(self):
        print(self.data[:self.length])

# 测试
arr = DynamicArray()
for i in range(5):
    arr.push(i + 1)
arr.print()  # 输出: [1, 2, 3, 4, 5]

📊 五、扩容策略的差异分析

编程语言默认初始容量扩容倍率特点
Java101.5x使用 Arrays.copyOf() 拷贝
C++02xvector 使用“翻倍策略”
Python4动态因子使用 C 扩容机制,策略复杂
JS不固定引擎优化取决于 V8 引擎实现

⚠️ 六、常见错误与陷阱

场景错误理解正确理解
扩容后数据丢失没有拷贝旧数组必须将旧数组元素拷贝进新数组
slice() 等偷懒方法模拟时需控制边界与容量用原始数组模拟更贴近底层逻辑
初始容量太小或太大导致频繁扩容或内存浪费建议初始容量设为 4 或 8

🎯 七、拓展任务

  • 给动态数组加上 insert(index, val) 插入功能
  • 支持 pop() 删除末尾元素
  • 设计支持“缩容”的智能数组(如空位太多就减半)

🧩 总结一句话

动态数组是几乎所有现代语言中最常用的数据结构之一,理解其扩容机制,是提升数据结构功力的第一步!


下一篇预告:

📘 第7篇:【从前端视角看图结构】用邻接表和邻接矩阵处理“页面跳转图”!