与向量和Strings一样,哈希map也是也是一种常见的集合。
HashMap<K,V>类型存储K类型的键到V类型的值的映射。它通过散列函数来完成此操作,该函数确定如何将这些键和值放置到内存中。
创建一个哈希map:
//创建一个空哈希map,并使用insert方法进行插值
fn main() {
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
println!("{:?}", scores);
}
D:\learn\cargo_learn>cargo run
Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
Finished dev [unoptimized + debuginfo] target(s) in 0.79s
Running `target\debug\cargo_learn.exe`
{"Blue": 10, "Yellow": 50}
请注意,我们首先需要使用标准库的collections部分中的HashMap。 在我们的三个常见集合中(vec, STring),该集合是最不常用的集合,因此未包含在序幕中自动纳入范围的功能中。 哈希图也没有得到标准库的支持。 例如,没有内置的宏可以构建它们。
特别哈希map的key与value都分别是一个vec(向量),所以务必保证key向量的类型一致,value向量的类型一致;
构造哈希图的另一种方法是在元组的向量上使用迭代器和collect方法,其中每个元组都由一个键及其值组成。 collect方法将数据收集到多种收集类型中,包括HashMap。 例如,如果我们在两个单独的向量中具有名称和初始值,则可以使用zip方法创建一个元组向量,其中"Blue"与10配对,依此类推。 然后,我们可以使用collect方法将元组的矢量转换为哈希图,如下例所示:
fn main() {
use std::collections::HashMap;
let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
let mut scores: HashMap<_, _> =
teams.into_iter().zip(initial_scores.into_iter()).collect();
println!("{:#?}", scores)
}
D:\learn\cargo_learn>cargo run
Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
warning: variable does not need to be mutable
--> src\main.rs:7:9
|
7 | let mut scores: HashMap<_, _> =
| ----^^^^^^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` on by default
warning: 1 warning emitted
Finished dev [unoptimized + debuginfo] target(s) in 0.82s
Running `target\debug\cargo_learn.exe`
{
"Yellow": 50,
"Blue": 10,
}
这里需要使用类型注释HashMap <_,_>,因为它可以收集到许多不同的数据结构中,并且Rust除非您指定,否则都不知道您想要哪个。 但是,对于键和值类型的参数,我们使用下划线,Rust可以根据向量中数据的类型推断哈希映射包含的类型。 在上例中,键类型将为String,而值类型将为i32。
哈希map与所有权
对于实现Copy特征的类型,例如i32,这些值将被复制到哈希map中。 对于诸如String之类的拥有的值,这些值将被移动,并且哈希映射将成为这些值的所有者,如下例所示:
fn main() {
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与field_value在此处及其之后就不可用了,因为他们移动了,移动给了map;
}
如果我们在哈希map中插入对值的引用,则这些值将不会移入哈希图中。 引用所指向的值必须至少在哈希映射有效期间才有效。
在哈希map中访问值
通过将其key提供给get方法,我们可以从哈希图中获得一个值:
fn main() {
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);
println!("{:?}", scores);
println!("{:?}", team_name);
println!("{:?}", score);
}
D:\learn\cargo_learn>cargo run
Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
Finished dev [unoptimized + debuginfo] target(s) in 0.74s
Running `target\debug\cargo_learn.exe`
{"Yellow": 50, "Blue": 10}
"Blue"
Some(10)
我们可以像使用向量一样,使用for循环对哈希映射中的每个键/值对进行迭代:
fn main() {
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);
}
}
D:\learn\cargo_learn>cargo run
Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
Finished dev [unoptimized + debuginfo] target(s) in 0.75s
Running `target\debug\cargo_learn.exe`
Yellow: 50
Blue: 10
更新哈希map
尽管键和值的数量是可以增加的,但每个键一次只能具有一个关联的值。 当想更改哈希map中的数据时,必须确定在键已分配值的情况下如何处理这种情况。可以用新值替换旧值,而完全不考虑旧值;可以保留旧值,而忽略新值,仅在键尚无值时才添加新值;或者,可以将旧值和新值结合起来。让我们看看如何做这些!
替换旧值
通过insert方法替换哈希map中的值,不管是否已经存在该属性:
fn main() {
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);
println!("{:?}", scores);
}
D:\learn\cargo_learn>cargo run
Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
Finished dev [unoptimized + debuginfo] target(s) in 0.78s
Running `target\debug\cargo_learn.exe`
{"Blue": 25}
仅当键没有值时才插入值
通过entry
方法与or_insert
方法相结合在哈希map中插入新纪录如果有老的记录则不作任何操作:
fn main() {
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);
}
D:\learn\cargo_learn>cargo run
Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
Finished dev [unoptimized + debuginfo] target(s) in 0.76s
Running `target\debug\cargo_learn.exe`
{"Yellow": 50, "Blue": 10}
entry方法的返回值是一个称为Entry的枚举,它表示一个可能存在或可能不存在的值。
根据旧值更新值
哈希映射的另一个常见用例是查找键的值,然后根据旧值对其进行更新。 例如,下例显示了用于计算每个单词在某些文本中出现的次数的代码。 我们使用以单词为键的哈希map,并增加值以跟踪我们看到该单词的次数。如果这是我们第一次看到一个字,我们将首先插入值0:
fn main() {
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);
}
D:\learn\cargo_learn>cargo run
Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
Finished dev [unoptimized + debuginfo] target(s) in 0.79s
Running `target\debug\cargo_learn.exe`
{"hello": 1, "wonderful": 1, "world": 2}
此代码将打印{"hello": 1, "wonderful": 1, "world": 2}
。or_insert方法实际上返回对此键的值的可变引用(&mut V)。 在这里,我们将该可变引用存储在count变量中,因此要分配给该值,我们必须首先使用星号(*)取消引用计数。可变引用在for循环结束时超出范围,因此所有这些更改都是安全的,并且借用规则允许。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。