AI Library · 知识库

# 顺序集合:收集任务与容量扩展

学习目标:理解动态列表、切片与固定数组在内存追加、索引访问与遍历上的核心差异。

前置知识与范围:基本数据类型与循环。演示在内存中动态维护有序的任务字符串列表。

# 问题与边界

模拟按到达顺序收集任务项:

  1. 初始化一个空的动态集合。
  2. 依次追加三个任务:"build", "test", "deploy"。
  3. 契约要求:集合保持插入顺序,总长度为 3;首个元素为 "build",末尾元素为 "deploy"。
  4. 越界访问必须在语言层面引发安全拦截(如抛出异常或返回 Option),不得产生未定义内存悬垂。

# 关键差异

语言 动态顺序结构 追加操作 长度与容量 越界保护机制
Rust Vec<T> vec.push(item) len() / capacity() vec[i] 索引越界触发 panic;vec.get(i) 返回 Option
Go 切片 []T slice = append(slice, item) len(s) / cap(s) 越界引发运行时 panic
Java ArrayList<T> list.add(item) size() IndexOutOfBoundsException
JavaScript Array arr.push(item) arr.length 越界访问返回 undefined,不报错!
TypeScript 沿用 JS Array arr.push(item) arr.length 默认类型为 T,越界返回 undefined 存在类型盲区
Kotlin ArrayList<T> / mutableListOf list.add(item) list.size IndexOutOfBoundsException
Python 原生 list list.append(item) len(l) IndexError
C++ std::vector<T> vec.push_back(item) vec.size() / vec.capacity() vec[i] 不检查越界(UB);vec.at(i) 抛出 std::out_of_range

# 各语言实现

# Rust

Rustfn main() {
    let mut tasks: Vec<&str> = Vec::new();
    tasks.push("build");
    tasks.push("test");
    tasks.push("deploy");

    assert_eq!(tasks.len(), 3);
    assert_eq!(tasks[0], "build");
    assert_eq!(tasks[2], "deploy");
    assert_eq!(tasks.get(3), None); // 安全访问边界

    println!("tasks: {}, first: {}", tasks.len(), tasks[0]);
}

Rust 的 Vec 在连续堆内存中动态增长;get() 返回安全保护的 Option。

# Go

Gopackage main

import "fmt"

func main() {
	var tasks []string
	tasks = append(tasks, "build")
	tasks = append(tasks, "test")
	tasks = append(tasks, "deploy")

	if len(tasks) != 3 || tasks[0] != "build" || tasks[2] != "deploy" {
		panic("mismatch")
	}

	fmt.Printf("tasks: %d, first: %s\n", len(tasks), tasks[0])
}

Go 的切片是对底层数组的轻量三元头(指针、长度、容量)。append 在容量不足时会分配新底层数组,因此必须重新赋值给切片变量。

# Java

Javaimport java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> tasks = new ArrayList<>();
        tasks.add("build");
        tasks.add("test");
        tasks.add("deploy");

        assert tasks.size() == 3;
        assert "build".equals(tasks.get(0));
        assert "deploy".equals(tasks.get(2));

        System.out.println("tasks: " + tasks.size() + ", first: " + tasks.get(0));
    }
}

Java 推荐面对接口 List 编程,底层由可扩容数组 ArrayList 支撑。

# JavaScript

JavaScriptconst assert = {
  equal: (a, b) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
  deepEqual: (a, b) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
  throws: (fn, errType) => {
    try { fn(); throw new Error("expected throw"); } catch (e) {
      if (e.message === "expected throw") throw e;
      if (errType && !(e instanceof errType)) throw e;
    }
  }
};

const tasks = [];
tasks.push("build");
tasks.push("test");
tasks.push("deploy");

assert.equal(tasks.length, 3);
assert.equal(tasks[0], "build");
assert.equal(tasks[2], "deploy");
assert.equal(tasks[3], undefined); // 越界返回 undefined

console.log(`tasks: ${tasks.length}, first: ${tasks[0]}`);

JavaScript 的数组是动态异构对象,索引越界不会抛出异常而是直接求值为 undefined。

# TypeScript

TypeScriptconst assert = {
  equal: (a: unknown, b: unknown) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
  deepEqual: (a: unknown, b: unknown) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
  throws: (fn: () => void, errType?: unknown) => {
    try { fn(); throw new Error("expected throw"); } catch (e: unknown) {
      if (e instanceof Error && e.message === "expected throw") throw e;
      if (typeof errType === "function" && !(e instanceof errType)) throw e;
    }
  }
};

const tasks: string[] = [];
tasks.push("build");
tasks.push("test");
tasks.push("deploy");

assert.equal(tasks.length, 3);
assert.equal(tasks[0], "build");
assert.equal(tasks[2], "deploy");

console.log(`tasks: ${tasks.length}, first: ${tasks[0]}`);

注意开启 noUncheckedIndexedAccess 时,TypeScript 会将 tasks[0] 视为 string | undefined 以防御越界假定。

# Kotlin

Kotlinfun main() {
    val tasks = mutableListOf<String>()
    tasks.add("build")
    tasks.add("test")
    tasks.add("deploy")

    check(tasks.size == 3)
    check(tasks[0] == "build")
    check(tasks[2] == "deploy")

    println("tasks: ${tasks.size}, first: ${tasks[0]}")
}

Kotlin 在类型层严格划分只读接口 List<T> 与可变接口 MutableList<T>。

# Python

Pythonif __name__ == "__main__":
    tasks = []
    tasks.append("build")
    tasks.append("test")
    tasks.append("deploy")

    assert len(tasks) == 3
    assert tasks[0] == "build"
    assert tasks[-1] == "deploy" # 支持负索引反向访问

    print(f"tasks: {len(tasks)}, first: {tasks[0]}")

Python 的内置列表类型底层为动态指针数组,支持通过负数索引反向访问末尾项。

# C++

C++#include <iostream>
#include <vector>
#include <string>
#include <cassert>

int main() {
    std::vector<std::string> tasks;
    tasks.push_back("build");
    tasks.push_back("test");
    tasks.push_back("deploy");

    assert(tasks.size() == 3);
    assert(tasks[0] == "build");
    assert(tasks.at(2) == "deploy");

    std::cout << "tasks: " << tasks.size() << ", first: " << tasks[0] << "\n";
}

C++ 中 operator[] 出于性能不进行范围检查,若需要越界安全检查应使用成员函数 at()。

# 迁移误区与适用限制

# 运行与验证

语言版本基线见示例基线。 所有程序运行后控制台输出均为:

纯文本tasks: 3, first: build

元素顺序、长度与索引访问断言全部通过。

# 相关阅读