HashMap
约 1746 字大约 6 分钟
2025-08-03
HashMap 是 Rust 标准库中提供的一种基于哈希表实现的键值对集合类型,位于 std::collections 模块中。HashMap<K, V> 允许通过键来快速查找对应的值,适用于需要根据 任意类型的键 来存储和检索数据的场景,而不是像 Vector 那样通过索引访问
重要
与 Vector 类似,Hash Map 的 键值与键值、值与值之间类型必须都是相同类型
创建 Hash Map
HashMap::new
创建一个 Hash Map 使用 new 方法
use std::collections::HashMap;
fn main() {
let mut hash_map = HashMap::new();
}提示
Hash Map 较另外两种方式相比使用较少,所以并没有被 prelude 自动引用。因此在使用时需要导入标准库中集合部分的 HashMap,且没有内建的宏
HashMap::with_capacity
HashMap 也会根据元素内容动态扩容。如果提前知道大概会插入多少键值对,可以使用 HashMap::with_capacity 预先分配容量,从而减少后续扩容带来的额外开销
预分配 Hash Map 容量
use std::collections::HashMap;
fn main() {
let mut scores: HashMap<String, i32> = HashMap::with_capacity(10);
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
println!("{scores:?}");
}插入与更新
insert 插入键值对
向 HashMap 中增加元素使用 insert 方法,调用该方法将会返回一个 Option<V>,首次插入时返回 None。
增加元素
use std::collections::HashMap;
fn main() {
let mut hash_map = HashMap::new();
hash_map.insert(String::from("Blue"), 10);
hash_map.insert(String::from("Yellow"), 50);
println!("{:?}", hash_map);
}重复键的覆盖行为
使用相同的键多次 insert 会覆盖旧值,此时 Options 将返回被覆盖的旧值 Some(旧值)
重复插入并判断是否覆盖
use std::collections::HashMap;
fn main() {
let mut hash_map = HashMap::new();
hash_map.insert(String::from("Blue"), 10);
hash_map.insert(String::from("Yellow"), 50);
// 如果返回 Some(old),说明此键已存在且被覆盖;None 表示第一次插入
let prev = hash_map.insert(String::from("Yellow"), 60);
if let Some(old) = prev {
println!("替换发生,旧值: {old}");
} else {
println!("没有覆盖,这是第一次插入该键");
}
println!("{:?}", hash_map);
}遍历与读取 HashMap
get 获取值
HashMap 获取值的方式也是通过 get 方法,其同样也会返回一个 Option<&V> 类型
get 方法获取值
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("Blue");
// get 首先会返回一个 Option<&i32>
// 然后 copied 方法会把 Option<&i32> 变为 Option<i32>
// 以便 get 获取为空时设置默认值
// 如果不 copied 则可以直接 .unwrap_or(&0);
let score = scores.get(&team_name).copied().unwrap_or(0);
}get_mut
get 方法只能获取不可变引用,如果需要修改某个 key 对应的 value,可以使用 get_mut,该方法返回 Option<&mut V>。
get_mut 修改值
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
if let Some(score) = scores.get_mut("Blue") {
*score += 10;
}
println!("{scores:?}");
}get_key_value 获取键值对
get_key_value 用于同时拿到 HashMap 中实际存储的 key 和 value。该方法返回 Option<(&K, &V)>。
get_key_value 获取键值对
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("Blue");
if let Some((key, value)) = scores.get_key_value(&team_name) {
println!("{key}: {value}");
}
}for 循环遍历
可以通过 for 循环遍历 HashMap 中的键值对。
for 循环遍历 HashMap
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
for (key, value) in &scores {
println!("{key}: {value}");
}
}提示
HashMap 不保证遍历顺序。因为 它底层是基于哈希表实现的,元素存储顺序不等于插入顺序。如果需要按照 key 的顺序遍历,可以使用 BTreeMap,它会根据键的顺序进行存储和遍历
常见遍历方法
| 方法名称 | 返回内容 | 是否消费所有权 | 说明 |
|---|---|---|---|
iter() | (&K, &V) | 否 | 只读遍历键值对,遍历结束后 HashMap 仍然可用 |
iter_mut() | (&K, &mut V) | 否 | 遍历键值对,并允许修改 value |
into_iter() | (K, V) | 是 | 取走整个 HashMap 的所有权,遍历后原变量不可再用 |
keys() | &K | 否 | 只遍历 key 的不可变引用 |
values() | &V | 否 | 只遍历 value 的不可变引用 |
values_mut() | &mut V | 否 | 只遍历 value 的可变引用,可以修改 value |
into_keys() | K | 是 | 消费 HashMap,只取出所有 key |
into_values() | V | 是 | 消费 HashMap,只取出所有 value |
entry API
entry API 是 HashMap 提供的一套强大接口,用于在访问或修改某个键对应的值时,能够同时处理该键是否存在的情况。通过 entry 方法可以获取一个 Entry 枚举,进而调用 or_insert、or_insert_with、and_modify 等方法来实现缺省插入、基于旧值修改等功能。
Entry 有两种情况:
- 键不存在:
Entry::Vacant,可以选择插入一个默认值 - 键存在:
Entry::Occupied,可以直接访问或修改现有值
if let Some(value) = score.get_mut("Blue") {
*value += 1;
} else {
score.insert("Blue", 1);
}or_insert
该方法会检查当前键是否存在,如果不存在则插入提供的默认值,并返回该键对应值的可变引用;如果键已存在,则直接返回现有值的可变引用。
重要
or_insert 返回的是 &mut V 也就是该 Key 对应的可变引用
or_insert 示例
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(100);
println!("{scores:?}");
}Yellow 原来不存在,所以会插入默认值 50,Blue 原来已经存在,所以不会被 100 覆盖
or_insert_with
该方法与 or_insert 类似,但接受一个闭包作为参数,只有在当前键不存在时才会调用该闭包来生成默认值。这对于默认值的构造开销较大时非常有用,可以避免不必要的计算。
or_insert_with 示例
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores
.entry(String::from("Blue"))
.or_insert_with(|| {
println!("Blue 键不存在,生成默认值");
100
});
println!("{scores:?}");
}因为 Blue 已经存在,所以闭包不会执行
and_modify
该方法用于当 key 已经存在时,修改已有值;如果 key 不存在,则什么也不做。
and_modify 示例
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores
.entry(String::from("Blue"))
.and_modify(|score| *score += 1);
.or_insert(50);
scores
.entry(String::from("Yellow"))
.and_modify(|score| {
*score += 5;
})
.or_insert(0);
println!("{scores:?}");
}Blue 已经存在,所以在原来的 10 上加 1;Yellow 不存在,所以插入默认值 0
Hash Map 与 所有权
对于像 i32 这种实现了 Copy Trait 的类型,其值可以拷贝进 Hash Map。而对于像 String 这样拥有所有权的值,其值将被移动而 Hash Map 会成为这些值的所有者,因此如果后续需要继续使用,则需调用 clone 或 &
Hash Map 与 所有权
use std::collections::HashMap;
fn main() {
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut hash_map = HashMap::new();
hash_map.insert(field_name, field_value);
// 后续 field_name 与 field_value 将无法继续使用
}