
push 和 get。本文从零梳理 Rust 核心集合类型的选用逻辑、实战技巧与常见坑点,帮你真正"掌握"而非"会用"。Rust 标准库提供的集合并不多,但每一个都有明确的设计意图:
类型 | 底层结构 | 有序? | 适用场景 |
|---|---|---|---|
Vec<T> | 动态数组 | ✅ 按插入顺序 | 绝大多数默认选择 |
VecDeque<T> | 环形队列 | ✅ | 两端频繁增删 |
LinkedList<T> | 双向链表 | ✅ | 中间频繁插入/删除 |
HashMap<K,V> | 哈希表 | ❌ | 按 Key 快速查找 |
BTreeMap<K,V> | 红黑树 | ✅ 按 Key 排序 | 需要有序遍历 |
HashSet<T> | 哈希表 | ❌ | 去重 + 快速判断存在 |
BTreeSet<T> | 红黑树 | ✅ 排序去重 | 需要有序去重 |
BinaryHeap<T> | 二叉堆 | ❌ | 优先级队列 |
选型的第一原则:不知道选什么,就选 Vec 或 HashMap,覆盖 80% 场景。
Vec 是 Rust 中使用频率最高的类型,但很多人只用了它 20% 的能力。
with_capacity 避免重复分配rust// ❌ 每次 push 都可能触发扩容 + 拷贝
let mut v = Vec::new();
for i in 0..10000 {
v.push(i);
}
// ✅ 提前分配,零额外开销
let mut v = Vec::with_capacity(10000);
for i in 0..10000 {
v.push(i);
}
with_capacity不改变len,只预分配内存。这在已知数据量时是必用优化。
extend 比循环 push 快得多rustlet mut v = vec![1, 2, 3];
let more = vec![4, 5, 6];
// ✅ 一次性extend,内部会用 memcpy 优化
v.extend(more);
// ❌ 逐个 push,无法批量优化
for x in more {
v.push(x);
}drain 是被严重低估的方法rustlet mut v = vec![1, 2, 3, 4, 5];
// 删除索引 1..3 的元素,并拿到被删除的部分
let drained = v.drain(1..3);
// v 现在是 [1, 4, 5]
// drained 是 [2, 3]
// 常见用法:批量删除满足条件的元素
v.retain(|&x| x % 2 == 0); // 只保留偶数drain 的性能远优于 remove + 循环,因为它是一次性内存移动,而不是逐个前移。
[] 取值会 panicrustlet mut map = HashMap::new();
map.insert("key", 42);
// ❌ 如果 key 不存在,直接 panic
let v = map["key"];
// ✅ 用 get,返回 Option
if let Some(&v) = map.get("key") {
println!("{}", v);
}
// ✅ 用 entry API,原子性地"获取或插入"
let v = map.entry("key").or_insert(0);rustlet mut map = HashMap::new();
map.insert(1, "a");
map.insert(2, "b");
// ❌ 编译错误:不能同时可变借用和不可变借用
for (k, v) in &map {
map.remove(&k);
}
// ✅ 方案一:先收集 keys
for k in map.keys().cloned().collect::<Vec<_>>() {
map.remove(&k);
}
// ✅ 方案二:用 drain(最优雅)
map.drain();entry API 是 HashMap 的瑞士军刀rustlet mut map: HashMap<&str, i32> = HashMap::new();
// 场景1:计数
for word in &["apple", "banana", "apple", "cherry", "banana", "apple"] {
*map.entry(word).or_insert(0) += 1;
}
// map = {"apple": 3, "banana": 2, "cherry": 1}
// 场景2:只在不存在时插入
map.entry("date").or_insert(1);
// 场景3:修改已存在的值
map.entry("apple").and_modify(|c| *c += 10);
entryAPI 相比get + insert的核心优势:只查一次哈希。 在高频操作中这个差异很明显。
对比项 | HashSet | BTreeSet |
|---|---|---|
查找 | O(1) 平均 | O(log n) |
有序遍历 | ❌ 随机 | ✅ 升序 |
最小/最大值 | O(n) 遍历 | O(log n) |
适用 | 纯去重、存在性判断 | 需要范围查询、有序输出 |
rustuse std::collections::{HashSet, BTreeSet};
let mut hs = HashSet::new();
let mut bs = BTreeSet::new();
for i in [5, 1, 9, 3, 7] {
hs.insert(i);
bs.insert(i);
}
// HashSet 遍历顺序不确定
println!("{:?}", hs.iter().collect::<Vec<_>>()); // 可能是 [1, 3, 5, 7, 9] 也可能乱序
// BTreeSet 保证升序
println!("{:?}", bs.iter().collect::<Vec<_>>()); // 一定是 [1, 3, 5, 7, 9]
// 需要范围查询?只能用 BTreeSet
let range: Vec<_> = bs.range(3..=7).copied().collect();
// [3, 5, 7]iter() 链式操作替代手动循环rustlet v = vec![1, 2, 3, 4, 5, 6];
// 手动循环
let mut sum = 0;
for &x in &v {
if x % 2 == 0 {
sum += x * x;
}
}
// ✅ 链式迭代器,零中间分配
let sum: i32 = v.iter()
.filter(|&&x| x % 2 == 0)
.map(|&x| x * x)
.sum();collect() 的类型推导rustlet v = vec!["1", "2", "3"];
// ❌ 编译报错:类型不明确
let nums: Vec<i32> = v.iter().map(|s| s.parse()).collect();
// ✅ turbofish 显式指定
let nums: Vec<i32> = v.iter()
.map(|s| s.parse().unwrap())
.collect();
// ✅ 或用 collect 直接推断(更常用)
let nums: Vec<i32> = v.iter()
.filter_map(|s| s.parse().ok())
.collect();retain 代替 filter + collectrustlet mut v = vec![1, 2, 3, 4, 5, 6];
// filter + collect:分配新 Vec
let evens: Vec<_> = v.iter().filter(|&&x| x % 2 == 0).copied().collect();
// ✅ retain:原地修改,零分配
v.retain(|&x| x % 2 == 0);
retain在大数据量下优势明显,因为它不分配新内存。
你的需求 | 推荐类型 | 原因 |
|---|---|---|
随机访问 + 尾部增删 | Vec | 缓存友好,O(1) 尾部操作 |
头部 + 尾部增删 | VecDeque | 两端 O(1),中间 O(n) |
中间频繁插入/删除 | LinkedList | O(1) 插入删除,但缓存不友好 |
Key-Value 查找 | HashMap | 平均 O(1),无序 |
Key-Value + 有序遍历 | BTreeMap | O(log n),有序 |
去重 + 快速查找 | HashSet | 平均 O(1) |
去重 + 有序 + 范围查询 | BTreeSet | O(log n) |
优先级队列 | BinaryHeap | O(log n) 插入,O(1) 取最大 |
Rust 集合类型的设计哲学是 "零成本抽象"——每种类型都有明确的性能特征和适用边界。掌握它们的关键不是记住 API,而是理解 每种结构在内存中是怎么组织的,这样你才能在写代码时本能地做出正确选择。
下次再遇到"该用什么集合"的问题,先问自己三个问题:要不要有序?要不要按 Key 查?要不要频繁中间插入? 答案就出来了。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。