Rust的Vector

了解 Rust 的 Vec<T>

Rust 是一门强调安全性、性能和并发的现代编程语言。在 Rust 中,Vec<T> 是一个动态大小的数组类型,广泛应用于需要动态存储的数据结构中。Vec 是 Rust 标准库中非常重要的一部分,它提供了灵活和高效的内存管理方式。

什么是 Vec<T>

Vec<T> 是 Rust 的一个集合类型,属于 "动态数组"。它可以动态增长或缩小,存储类型为 T 的元素。这意味着 Vec<T> 在运行时可以根据需求调整大小,而不像传统的数组那样大小固定。

在 Rust 中,Vec<T> 是一个堆分配的数据结构,与栈上的固定大小数组不同。它允许你在运行时增加或删除元素。

Vec 与传统数组的对比

Rust 中的数组是固定大小的,这意味着在定义时必须指定大小并且不能更改。相比之下,Vec 提供了灵活的存储能力,你可以根据需要动态地增加或删除元素,适用于一些无法预知元素数量的场景。

fn main() {
    // 数组
    let arr = [1, 2, 3]; // 固定大小

    // Vec
    let mut vec = Vec::new();  // 创建一个空的 Vec
    vec.push(1);               // 向 Vec 中添加元素
    vec.push(2);
    vec.push(3);

    // 输出 Vec
    println!("{:?}", vec);    // 打印: [1, 2, 3]
}

在上面的示例中,arr 是一个固定大小的数组,而 vec 是一个 Vec,你可以随时调用 push() 方法向其中添加元素。

Vec 的常用操作

1. 创建和初始化 Vec

Vec::new()

创建一个空的 Vec,它的初始大小为 0。

let mut v: Vec<i32> = Vec::new();

vec![]

通过 vec![] 宏快速创建并初始化一个 Vec

let v = vec![1, 2, 3, 4];

Vec::with_capacity(n)

创建一个具有指定容量的空 Vec,可以避免在添加元素时频繁的重新分配内存。

let mut v = Vec::with_capacity(10);

2. 访问和修改元素

get(index)

返回 Vec 中指定索引处的元素,返回 Option 类型,避免越界错误。如果索引越界,返回 None

let v = vec![1, 2, 3];
let item = v.get(1);  // Some(2)

[](索引)

使用索引来访问 Vec 中的元素,若索引越界,会发生 panic。

let v = vec![1, 2, 3];
let item = v[1];  // 2

push(value)

Vec 的末尾添加一个元素。

let mut v = vec![1, 2];
v.push(3);  // v 变成 [1, 2, 3]

insert(index, value)

在指定索引处插入一个元素。其后的元素将右移。

let mut v = vec![1, 2, 4];
v.insert(2, 3);  // v 变成 [1, 2, 3, 4]

remove(index)

删除指定索引的元素,并返回它。删除操作会使得该元素后的元素左移。

let mut v = vec![1, 2, 3, 4];
let removed = v.remove(1);  // removed = 2, v 变成 [1, 3, 4]

pop()

Vec 的末尾移除并返回一个元素,若 Vec 为空,返回 None

let mut v = vec![1, 2, 3];
let last = v.pop();  // last = Some(3), v 变成 [1, 2]

resize(new_len, value)

调整 Vec 的大小,如果 new_len 大于当前长度,会用 value 填充新增加的元素;如果 new_len 小于当前长度,会丢弃多余的元素。

let mut v = vec![1, 2, 3];
v.resize(5, 0);  // v 变成 [1, 2, 3, 0, 0]

3. 其他常用方法

len()

返回 Vec 中的元素个数。

let v = vec![1, 2, 3];
let length = v.len();  // length = 3

is_empty()

检查 Vec 是否为空。

let v = vec![];
let is_empty = v.is_empty();  // is_empty = true

clear()

清空 Vec 中的所有元素。

let mut v = vec![1, 2, 3];
v.clear();  // v 变成 []

contains(&value)

检查 Vec 是否包含指定的元素。

let v = vec![1, 2, 3];
let has_two = v.contains(&2);  // has_two = true

capacity()

返回 Vec 当前分配的容量(即它在堆上可以存储多少个元素而不需要重新分配内存)。

let v = vec![1, 2, 3];
let cap = v.capacity();  // cap = 3

reserve(additional)

提前为 Vec 分配额外的内存,避免后续的重新分配操作。additional 是希望预分配的额外空间。

let mut v = Vec::new();
v.reserve(10);  // 预分配至少可以容纳 10 个元素

shrink_to_fit()

Vec 的容量缩减到当前的长度,以释放多余的内存。

let mut v = Vec::with_capacity(100);
v.push(1);
v.shrink_to_fit();  // 将容量缩小到 1

split_at(mid)

Vecmid 索引分割为两个部分,返回一个元组,包含前半部分和后半部分。

let v = vec![1, 2, 3, 4, 5];
let (first, second) = v.split_at(3);
// first = [1, 2, 3], second = [4, 5]

clone()

克隆 Vec,即深拷贝一个新的 Vec

let v = vec![1, 2, 3];
let v_clone = v.clone();  // v_clone = [1, 2, 3]

4. 遍历和映射

iter()

返回一个不可变的迭代器,允许你遍历 Vec 中的元素。

let v = vec![1, 2, 3];
for x in v.iter() {
    println!("{}", x);
}

into_iter()

返回一个可以消耗 Vec 的迭代器,移走元素。使用后,Vec 会被移除,无法再访问。

let v = vec![1, 2, 3];
for x in v.into_iter() {
    println!("{}", x);
}
// v 在这里已经被消耗掉

iter_mut()

返回一个可变的迭代器,允许你修改 Vec 中的元素。

let mut v = vec![1, 2, 3];
for x in v.iter_mut() {
    *x *= 2;
}
println!("{:?}", v);  // 输出: [2, 4, 6]

map()

通过对 Vec 中的每个元素应用函数,生成一个新的集合。

let v = vec![1, 2, 3];
let v2: Vec<_> = v.iter().map(|x| x * 2).collect();  // v2 = [2, 4, 6]

5. 其他操作

extend(iterable)

将另一个集合的元素添加到 Vec 的末尾。

let mut v = vec![1, 2];
let v2 = vec![3, 4];
v.extend(v2);  // v 变成 [1, 2, 3, 4]

retain(pred)

保留满足条件的元素,删除不满足条件的元素。

let mut v = vec![1, 2, 3, 4, 5];
v.retain(|&x| x % 2 == 0);  // v 变成 [2, 4]

sort()

Vec 中的元素进行排序。

let mut v = vec![3, 1, 4, 1, 5];
v.sort();  // v 变成 [1, 1, 3, 4, 5]

Vec<T> 的内存管理

Rust 的 Vec<T> 是一个智能指针,它在堆上分配内存。Rust 会自动管理 Vec 的内存,当 Vec 被销毁时,它所占用的内存会自动被释放,避免了内存泄漏。

Vec 内部有一块连续的内存,用来存储所有元素。当 Vec 增长时,如果没有足够的空间来容纳新元素,Rust 会自动重新分配更多的内存。这种增长通常是指数级的,因此可以有效减少内存重新分配的次数。

内存拷贝与优化

由于 Vec 在内部使用堆分配内存,当元素增多时,可能会导致内存重新分配和拷贝操作。如果你事先知道 Vec 的大小,可以通过 Vec::with_capacity() 方法来预分配内存,避免在增长过程中频繁的内存重新分配。

fn main() {
    // 创建一个初始容量为 10 的 Vec
    let mut vec = Vec::with_capacity(10);
    vec.push(1);
    vec.push(2);
    
    // 检查当前容量
    println!("当前容量: {}", vec.capacity());
}

总结

优势:

  • 动态大小:无需事先知道元素个数。
  • 高效内存管理:Rust 自动管理内存,减少了内存泄漏的风险。
  • 灵活性:支持许多常用操作,如添加、删除、访问、遍历等。

缺点:

  • 性能损失:由于 Vec 在堆上分配内存,相比栈上数组可能会略慢,尤其是在频繁增长或收缩时。
posted @ 2024-11-30 21:57  悲三乐二  阅读(149)  评论(1)    收藏  举报