【Rust自学】8.6. HashMap Pt.2:更新HashMap

发布时间:2026/7/23 18:50:09
【Rust自学】8.6. HashMap Pt.2:更新HashMap 8.6 HashMap Pt.2更新HashMap8.6.0. 本章内容第八章主要讲的是 Rust 中常见的集合。Rust 提供了很多集合类型的数据结构这些集合可以包含很多值。但是第八章所讲的集合与数组和元组有所不同。第八章中的集合是存储在堆内存上而非栈内存上的这也意味着这些集合的数据大小无需在编译时就确定在运行时它们可以动态地变大或变小。本章主要会讲三种集合Vector、String 和HashMap本文。8.6.1. 更新 HashMapHashMap的大小可变指的是其中的键值对数量可变。但是在任意时刻一个键只能对应一个值。当想要更新HashMap中的数据时可能有这么几种情况- 想要更新的键在HashMap中已经存在对应的值- 用新的值替换现有的值- 保留现有的值忽略新的值- 合并现有的值和新的值也就是说对现有的值进行修改键不存在添加一对键和值1. 替换现有的值如果向HashMap插入一对键值对但键已经存在程序就会把新值赋给这个键覆盖旧值。如下例use std::collections::HashMap; fn main() { let mut scores HashMap::new(); scores.insert(String::from(dev1ce), 0); scores.insert(String::from(dev1ce), 60); println!({:?}, scores); }这里为同一个键赋了两次值第一次是0第二次是60。第一次的值会被第二次覆盖也就是说最终dev1ce对应的值是60。输出{dev1ce: 60}2. 只在键不存在值时才插入这是最常见的情况。对于这种情况首先需要检查原HashMap中是否已经存在这个键如果不存在再插入新值。Rust 提供了entry方法来检查原HashMap中是否已经存在这个键。它的参数是键返回值是一个Entry枚举表示值是否存在。看个例子use std::collections::HashMap; fn main() { let mut scores HashMap::new(); scores.insert(String::from(dev1ce), 0); let e scores.entry(String::from(dev1ce)); println!({:?}, e); }这是键已经存在的情况。输出Entry(OccupiedEntry { key: dev1ce, value: 0, .. })也就是说如果键已经存在那么entry方法会返回一个占用中的 entryOccupiedEntry并关联已经存在的键值对。再试一下键不存在的情况。代码如下use std::collections::HashMap; fn main() { let mut scores HashMap::new(); scores.insert(String::from(dev1ce), 0); let e scores.entry(String::from(Zywoo)); println!({:?}, e); }输出Entry(VacantEntry(Zywoo))如果键不存在就会返回一个空缺的 entryVacantEntry并关联这个新的键。现在有办法检查原HashMap中是否已经存在这个键了那么如何根据是否存在来决定插入或不插入呢Rust 在Entry上提供了or_insert方法其参数是想要添加的值。它会根据 entry 是占用还是空缺来决定是否插入如果 entry 已被占用键已存在就保留现有值、不插入新值如果 entry 空缺键不存在就插入传入的值。最重要的一点是它有返回值该键对应值的可变引用。如果键已存在就返回HashMap中原有值的可变引用如果键不存在就先插入键值对再返回插入值的可变引用。利用这个特性可以实现一些简单的计数器后文会讲。看下例子use std::collections::HashMap; fn main() { let mut scores HashMap::new(); scores.insert(String::from(dev1ce), 0); scores.entry(String::from(Zywoo)).or_insert(100); scores.entry(String::from(dev1ce)).or_insert(60); println!({:?}, scores); }第一个entry语句查找Zywoo没有找到就返回空缺的 entryor_insert会根据该键和传入的参数100创建(Zywoo, 100)这个键值对。第二个entry语句查找dev1ce已经找到就返回占用中的 entryor_insert不会插入新值因此(dev1ce, 0)保持不变。输出键的顺序可能不同{Zywoo: 100, dev1ce: 0}如果这么讲还有些复杂那么你可以把scores.entry(String::from(Zywoo)).or_insert(100);看作两行代码let e scores.entry(String::from(Zywoo)); e.or_insert(100);3. 基于现有值来更新先看例子use std::collections::HashMap; fn main() { let text Thats one small step for [a] man, one giant leap for mankind.; let mut map HashMap::new(); for word in text.split_whitespace() { let count map.entry(word).or_insert(0); *count 1; } println!({:#?}, map); }首先声明了一个包含一句话的字符串字面值赋给text。然后创建了名为map的HashMap。接着使用for循环。text.split_whitespace()把text分割成字符串迭代器再用for来遍历。遍历时判断每个单词是否已出现在map中出现了就不插入新值没出现就插入0作为该键的新值。关键是理解count因为or_insert的返回值是该键对应值的可变引用所以每出现一次这个单词就把可变引用解引用后加1相当于完成一次计数。8.6.2. Hash 函数在默认情况下HashMap使用加密强度较高的哈希函数可以抵抗拒绝服务DoS攻击。但这种函数并不是可用的最快哈希算法它的优势是具有更好的安全性。如果觉得它性能不够好也可以指定不同的 hasher 来切换到另一个函数。hasher 指的是实现了BuildHashertrait 的类型。