Rust HashMap
HashMap 以键值对的形式存储数据:你按键(key)来查找,而不是像数组或 vector 那样按位置查找。如果你用过 Python 的字典,或者在 JavaScript 里把对象当成映射来用,那就是同一个思路。
创建 HashMap
Section titled “创建 HashMap”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new();
ages.insert("小红", 30); ages.insert("小刚", 25);
println!("{:?}", ages);}开头的 use std::collections::HashMap; 把这个类型引入作用域。和 Vec、String 不同,HashMap 默认是用不了的,所以这一行必不可少。
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("小红", 30);
match ages.get("小红") { Some(age) => println!("小红今年 {age} 岁"), None => println!("没找到"), }}.get() 返回的是一个 Option,而不是值本身,因为这个键可能不存在。Option 会在它自己的那一课里详细讲。现在只要知道,用 match 配合 Some/None 就是处理它的标准写法。
用同一个键再插入一次,就会覆盖旧的值:
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("小红", 30); ages.insert("小红", 31); // 覆盖掉 30
println!("{:?}", ages.get("小红"));}如果只想在键还不存在时才插入值,就用 .entry():
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("小红", 30);
ages.entry("小红").or_insert(99); // 小红已经存在,所以什么都不做 ages.entry("小刚").or_insert(25); // 小刚还不存在,所以插入它
println!("{:?}", ages);}遍历 HashMap
Section titled “遍历 HashMap”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("小红", 30); ages.insert("小刚", 25);
for (name, age) in &ages { println!("{name}今年 {age} 岁"); }}什么时候用 HashMap
Section titled “什么时候用 HashMap”只要你想按名字或 ID 查找东西,而不是按位置查找,就用 HashMap:统计单词出现的次数、把用户名映射到用户数据、按键缓存计算结果。如果顺序很重要,或者只是处理一个普通的列表,Vec 更合适。
1. 迷你电话簿
Section titled “1. 迷你电话簿”在一个 HashMap 里存三个名字和对应的电话号码。然后查找 "小王"(在电话簿里)和 "小赵"(不在电话簿里),为每个人打印一行友好的提示。提示:遍历 ["小王", "小赵"],再对 .get(name) 的结果用 match。
查看答案
use std::collections::HashMap;
fn main() { let mut phone_book = HashMap::new(); phone_book.insert("小李", "555-1234"); phone_book.insert("小王", "555-9876"); phone_book.insert("小陈", "555-4321");
for name in ["小王", "小赵"] { match phone_book.get(name) { Some(number) => println!("{name}:{number}"), None => println!("{name}:不在电话簿里"), } }}输出:
小王:555-9876小赵:不在电话簿里遍历我们想查的名字,而不是遍历整个 map,顺便还能得到固定的顺序。每当你需要 HashMap 给出可预测的输出时,这都是个好用的小技巧。
2. 统计投票
Section titled “2. 统计投票”你有这些投票:["红色", "蓝色", "红色", "绿色", "蓝色", "红色"]。统计每种颜色得了多少票,然后打印 "红色"、"绿色"、"蓝色" 和 "粉色"(一票都没有)的票数。提示:counts.entry(color).or_insert(0) 会给你这个颜色当前的票数,从 0 开始。用 *count += 1 给它加一。
查看答案
use std::collections::HashMap;
fn main() { let votes = ["红色", "蓝色", "红色", "绿色", "蓝色", "红色"]; let mut counts = HashMap::new();
for color in votes { let count = counts.entry(color).or_insert(0); *count += 1; }
for color in ["红色", "绿色", "蓝色", "粉色"] { match counts.get(color) { Some(n) => println!("{color}:{n}"), None => println!("{color}:0"), } }}输出:
红色:3绿色:1蓝色:2粉色:0or_insert(0) 返回的是 map 里那个值的可变引用,而不是一份副本。*count += 1 里的 * 表示“修改它指向的那个数字”,所以 map 本身就被更新了。“先 entry,没有就插入 0,再加 1”,这是 Rust 里计数的标准写法。
HashMap<K, V>存储键值对,要先用use std::collections::HashMap;把它引入作用域。.insert(key, value)添加或覆盖值,.get(key)查找值并返回一个Option。.entry(key).or_insert(value)只在键还不存在时才插入。- 遍历的顺序没有保证,不要依赖它。
小测验
1. 和 Vec、String 不同,要把 HashMap 引入作用域需要做什么?
2. .entry(key).or_insert(value) 是做什么的?
3. 遍历 HashMap 时,能依赖元素打印出来的顺序吗?
得分: 0 / 3
- 使用 Hash Map 储存键值对 —《Rust 程序设计语言》:讲键和值的所有权,还有一个更新值的单词计数例子。
- 散列表 HashMap — 通过例子学 Rust:一个可运行的通讯录例子,用到了
insert、get、remove和循环。 HashMap— 标准库文档(英文):全部方法,包括contains_key、remove、keys和values。Entry— 标准库文档(英文):.entry()能做的所有事情,比如or_insert_with和and_modify。