Skip to content
共 757 字预计阅读约 3 分钟

Rust HashMap:插入、查找与 entry API ​

更新时间:2026-09-20。HashMap 是最常用的键值集合,底层是罗宾汉开放寻址(非链表桶)。Vec 的内存布局见 Vec/String 内存布局,集合选型见 常用集合怎么选。

一、基本增删查 ​

rust
use std::collections::HashMap;
let mut scores: HashMap<String, i32> = HashMap::new();
scores.insert(String::from("blue"), 10);
scores.insert(String::from("red"), 50);

if let Some(v) = scores.get("blue") {       // get 返回 Option<&V>
    println!("{v}");
}
for (k, v) in &scores { println!("{k}={v}"); }
scores.remove("red");

get 返回 Option<&V> 而不是直接给值,查不到是 None 而非崩溃。key 类型必须实现 Eq + Hash(整数、String、&str 都满足)。

二、entry API:计数 / upsert 的正确姿势 ​

统计词频最典型。新手会先 get 再 insert,查两次还撞借用检查;entry 一次搞定:

rust
let mut count: HashMap<&str, u32> = HashMap::new();
for word in ["a", "b", "a", "c", "b", "a"] {
    *count.entry(word).or_insert(0) += 1;    // 有就取 &mut,没有就插 0
}
assert_eq!(count["a"], 3);

// 已存在才改、不存在才插的组合
count.entry("d")
    .and_modify(|v| *v += 1)
    .or_insert(1);
API作用
or_insert(v)空位插入 v,返回 &mut V
or_default()空位插入类型默认值(数字为 0)
or_insert_with(f)惰性构造,避免无谓分配
and_modify(f)键存在时对其值执行 f,可链式接 or_insert

三、所有权:String 与 &str 作 key ​

rust
let mut m = HashMap::new();
let name = String::from("alice");
m.insert(name, 20);          // String 被 move 进 map,之后 name 不可用
// m.insert(&name, 20);      // 若只借 &str,则 name 必须活得比 map 久
m.insert("bob".to_string(), 30);
let x = m.get("alice");      // 用 &str 查 String key:Borrow 自动适配

HashMap<String, V> 可以直接用 &str 调 get,靠的是 Borrow<str>,无需把查询串转成 String。

四、容量与迭代顺序 ​

rust
let mut m: HashMap<i32, i32> = HashMap::with_capacity(1000); // 已知规模,预留
  • HashMap 迭代顺序随机、不保证(RandomState 还带每进程随机种子,防哈希碰撞攻击);需要有序用 BTreeMap。
  • 预估条数用 with_capacity,避免反复 rehash。

五、常见坑 ​

  1. 循环里持有 &V 又 insert:可变借用与不可变借用冲突编译失败;先把要用的值 clone 出来或用 entry。
  2. get 后 unwrap:键不确定就 if let/match,确定存在才用 scores["k"](缺失会 panic)。
  3. 计数写成 get+insert:查两次且啰嗦,统一 entry().or_insert(0)。
  4. 依赖遍历顺序:HashMap 随机序,要排序遍历 key 用 BTreeMap 或自行收集排序。
  5. 多线程共享普通 HashMap:会编译失败/数据竞争,并发用 DashMap 或套 Mutex<HashMap>(见 Arc/Mutex/RwLock)。

总结 ​

Rust HashMap 用 new/insert/get(返回 Option<&V>)操作,key 必须 Eq+Hash、底层开放寻址且遍历顺序随机,已知规模用 with_capacity 预留;计数和「存在则改、不存在则插」一律用 entry().or_insert/and_modify,避免 get 后再 insert 的二次查找与借用冲突;String key 会拿走所有权但可用 &str 经 Borrow 查询,多线程共享不能用裸 HashMap,要套 Mutex/RwLock 或用 DashMap。