Rust 常用集合怎么选
更新时间:2026-09-20。Rust 标准库集合都在
std::collections(Vec/String因最常用被隐式引入 prelude)。选型的核心是访问模式:按下标/顺序?按键?要有序?要去重?Vec 内存布局见 Vec/String 内存布局。
一、选型决策表
| 需求 | 用什么 | 说明 |
|---|---|---|
| 一串同类型、顺序访问、尾部增删 | Vec<T> | 默认选择,缓存友好、最快 |
| 头部和尾部都要 O(1) 增删(队列/双端栈) | VecDeque<T> | 环形缓冲 |
| 键值、不要求有序 | HashMap<K,V> | 平均 O(1),见 HashMap/entry |
| 键值、要排序或范围查询 | BTreeMap<K,V> | B 树,按 key 有序,O(log n) |
| 去重、成员判断 | HashSet<T> / BTreeSet<T> | 本质是只存 key 的 map |
| 栈 LIFO | Vec::push/pop | 不必另找类型 |
| 队列 FIFO | VecDeque::push_back/pop_front | |
| 优先队列/取最大最小 | BinaryHeap<T> | 最大堆,pop 取最大 |
| 字符串 | String(=Vec<u8> + UTF-8 约束) | 见 String 与 &str |
二、为什么 LinkedList 基本不推荐
LinkedList<T> 虽是双向链表、任意位置插删 O(1),但:
- 每节点一次堆分配、节点在内存里离散,缓存局部性极差,遍历比 Vec 慢很多;
- 「中间插入 O(1)」还得先 O(n) 找到位置;
- Rust 所有权模型下,链表的双向指针写起来非常绕。
实践中双端需求用 VecDeque,几乎不用 LinkedList。标准库文档自己也建议:除非确有链表语义需求,否则选 Vec/VecDeque。
三、有序与去重:BTreeMap / Set
rust
use std::collections::{BTreeMap, HashSet};
let mut map = BTreeMap::new();
map.insert(3, "c"); map.insert(1, "a"); map.insert(2, "b");
for (k, v) in &map { println!("{k}={v}"); } // 永远 1,2,3 有序
for (k, v) in map.range(1..=2) { /* 范围查询 */ }
let mut set = HashSet::new();
for w in ["a","b","a","c"] { set.insert(w); }
assert_eq!(set.len(), 3); // 自动去重四、性能要点
- 优先 Vec:连续内存 + 单态化 + 预分配,是绝大多数数据容器的最优解,甚至很多「链表/队列」场景实测也是 Vec 快。
- 已知规模用
with_capacity:Vec/HashMap/String 都支持,避免反复扩容搬迁。 - 小集合别上 HashMap:几十个元素且 key 简单,线性 Vec 查找可能因缓存命中而更快。
- 需要稳定遍历顺序才用 BTree 系,或收集 key 后
sort。
五、常见坑
- 用 LinkedList 追潮流:性能反降,选 Vec/VecDeque。
- HashMap 当有序字典:遍历顺序随机,要有序用 BTreeMap。
- Vec 头部
remove(0)当队列:O(n) 搬移,FIFO 用 VecDeque。 - 频繁扩容不预留容量:循环 push/insert 前
with_capacity。 BinaryHeap当最小堆用反:默认最大堆,要最小可存Reverse(x)。
总结
Rust 集合按访问模式选:顺序数据和栈用 Vec(缓存最友好、默认首选)、头尾都要 O(1) 用 VecDeque、键值无序用 HashMap、要排序或范围查询用 BTreeMap、去重用 HashSet/BTreeSet、优先队列用 BinaryHeap;LinkedList 因逐节点堆分配和缓存局部性差基本不要碰,Vec 头部删元素是 O(n) 不能当队列;任何可增长集合在已知规模时都应用 with_capacity 预留,避免反复扩容。