HashMap高级特性
> 学习自定义类型作为键的方法,理解BTreeMap与HashMap的区别。
自定义类型作为 HashMap 的键
需要实现的 Trait
要使自定义类型可以作为 HashMap 的键,必须实现:
1. Eq + PartialEq - 用于比较键是否相等
2. Hash - 用于计算哈希值
┌─────────────────────────────────────────────────────┐
│ 自定义类型作为键的要求 │
├─────────────────────────────────────────────────────┤
│ │
│ 必须实现的 Trait: │
│ ├── PartialEq - 判断两个值是否相等 │
│ ├── Eq - 完全相等关系(自反、对称、传递) │
│ └── Hash - 计算哈希值 │
│ │
│ 推荐派生: │
│ #[derive(Debug, Clone, PartialEq, Eq, Hash)] │
│ │
│ 注意事项: │
│ • 字段都必须是 Hash 的 │
│ • 浮点数不能直接作为键(f32/f64 不实现 Hash) │
│ • 哈希值相同的键可能冲突 │
│ │
└─────────────────────────────────────────────────────┘使用 derive 自动派生
rust
▶ Runuse std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct User {
id: u32,
name: String,
}
fn main() {
let mut users = HashMap::new();
let user1 = User {
id: 1,
name: String::from("Alice"),
};
let user2 = User {
id: 2,
name: String::from("Bob"),
};
users.insert(user1.clone(), "alice@example.com");
users.insert(user2.clone(), "bob@example.com");
// 使用相同的键查找
let lookup = User {
id: 1,
name: String::from("Alice"),
};
match users.get(&lookup) {
Some(email) => println!("找到邮箱:{}", email),
None => println!("未找到用户"),
}
}手动实现 Hash
rust
▶ Runuse std::collections::HashMap;
use std::hash::{Hash, Hasher};
#[derive(Debug, PartialEq, Eq)]
struct Point {
x: i32,
y: i32,
}
// 手动实现 Hash
impl Hash for Point {
fn hash<H: Hasher>(&self, state: &mut H) {
self.x.hash(state);
self.y.hash(state);
}
}
fn main() {
let mut map = HashMap::new();
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 3, y: 4 };
map.insert(p1, "点 A");
map.insert(p2, "点 B");
// 查找
let lookup = Point { x: 1, y: 2 };
println!("{:?}", map.get(&lookup)); // Some("点 A")
}注意事项:浮点数不能作为键
rust
▶ Runuse std::collections::HashMap;
fn main() {
// ❌ 错误:f64 不实现 Hash
// let mut map: HashMap<f64, &str> = HashMap::new();
// map.insert(3.14, "pi");
// ✅ 解决方案 1:使用包装类型
use std::num::NonZeroU32;
// ✅ 解决方案 2:转换为整数(如果适用)
let mut map: HashMap<i64, &str> = HashMap::new();
map.insert((3.14 * 100.0) as i64, "pi");
// ✅ 解决方案 3:使用 BTreeMap(允许自定义比较)
use std::collections::BTreeMap;
let mut btree: BTreeMap<f64, &str> = BTreeMap::new();
btree.insert(3.14, "pi");
}小结
- 自定义类型作为键:必须实现
Hash + Eq + PartialEq,可用#[derive]派生 - 浮点数不能作为HashMap键:f32/f64不实现Hash,可转整数或用BTreeMap
- BTreeMap:有序存储,按键排序遍历,适合需要顺序的场景
练习题
详见:练习题