Rust 数据结构:HashMap
Rust 数据结构:HashMap
- Rust 数据结构:HashMap
- 创建一个新的哈希映射
- HashMap::new()
- 将元组变成哈希表
- 访问哈希映射中的值
- 哈希映射和所有权
- 更新哈希映射
- 重写一个值
- 仅当键不存在时才添加键和值
- 基于旧值更新值
- 散列函数
Rust 数据结构:HashMap
类型 HashMap<K, V> 使用散列函数存储 K 类型键到 V 类型值的映射。
创建一个新的哈希映射
HashMap::new()
创建空散列映射的一种方法是使用 new 和 insert 来添加元素。
use std::collections::HashMap;let mut scores = HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Yellow"), 50);
就像 vector 一样,哈希映射将它们的数据存储在堆上。它也是同构的:所有键必须具有相同的类型,所有值必须具有相同的类型。
将元组变成哈希表
use std::collections::HashMap;let vec = vec![("k1", "v1"), ("k2", "v2")];
let map: HashMap<_, _> = vec.into_iter().collect();
访问哈希映射中的值
我们可以通过向 get 方法提供键值来从哈希映射中获取值。
use std::collections::HashMap;let mut scores = HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Yellow"), 50);let team_name = String::from("Blue");let score = scores.get(&team_name).copied().unwrap_or(0);
在这里,score将为 10。get 方法返回一个 Option<&V>,如果该键在哈希映射中没有值,get 将返回 None。这个程序通过调用 copied 来处理 Option,得到一个 Option<i32>,而不是一个 Option<&i32>,然后 unwrap_or 将 score 设置为 0,如果 scores 没有对应键的条目。
我们可以用 for 循环迭代哈希映射中的每个键值对:
use std::collections::HashMap;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}");}
哈希映射和所有权
对于实现 Copy trait 的类型,比如 i32,值被复制到哈希映射中。对于 String,这些值将被移动,哈希映射将成为这些值的所有者。
use std::collections::HashMap;let field_name = String::from("Favorite color");let field_value = String::from("Blue");let mut map = HashMap::new();map.insert(field_name, field_value);// field_name and field_value are invalid at this point, try using them and// see what compiler error you get!
在调用 insert 将变量 field_name 和 field_value 移动到哈希映射之后,我们就不能使用它们了。
如果我们将值的引用插入到哈希映射中,这些值不会被移动到哈希映射中。引用所指向的值必须至少在哈希映射有效的时间内有效。
更新哈希映射
尽管键和值对的数量是可增长的,但每个唯一键一次只能有一个与之关联的值。
重写一个值
如果我们在哈希映射中插入一个键和一个值,然后用不同的值插入相同的键,那么与该键相关联的值将被替换。
use std::collections::HashMap;let mut scores = HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Blue"), 25);println!("{scores:?}");
仅当键不存在时才添加键和值
通常检查一个特定的键是否已经存在于哈希映射中并带有值,然后采取以下操作:如果键确实存在于哈希映射中,则现有的值应保持原样;如果键不存在,则插入它并为它插入一个值。
哈希映射为此有一个特殊的 API,称为 entry,它将您想要检查的键作为参数。entry 方法的返回值是一个名为 entry 的枚举,它表示一个可能存在也可能不存在的值。
use std::collections::HashMap;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(50);println!("{scores:?}");
or_insert 方法被定义为:如果对应的键存在,则返回对该键值的可变引用,如果不存在,则将该参数作为该键的新值插入,并返回对新值的可变引用。这种技术比我们自己编写逻辑要干净得多,而且与借用检查器配合得更好。
基于旧值更新值
哈希映射的另一个常见用例是查找键的值,然后根据旧值更新它。
use std::collections::HashMap;let text = "hello world wonderful world";let mut map = HashMap::new();for word in text.split_whitespace() {let count = map.entry(word).or_insert(0);*count += 1;}println!("{map:?}");
split_whitespace 方法返回一个迭代器,该迭代器覆盖文本中值的子片,这些子片由空格分隔。or_insert 方法返回一个对指定键值的可变引用(&mut V)。这里,我们将该可变引用存储在 count 变量中,因此为了对该值赋值,必须首先使用 * 解引用。可变引用在 for 循环结束时将超出作用域,因此所有这些更改都是安全的,并且是借用规则允许的。
散列函数
默认情况下,HashMap 使用名为 SipHash 的哈希函数,该函数可以抵抗涉及哈希表的拒绝服务(DoS)攻击。这不是最快的散列算法,但相对安全。
散列器是一种实现 BuildHasher trait 的类型,可以通过指定不同的哈希器来切换到另一个散列函数。