前言#
昨天我们学了String类型
今天往下学hash map
Hash Maps#
相信大家在别的语言中也学到或者用到过hash map,我们来看下rust中的hash map有什么不同。
前天讲vector的时候有说过hash map是map的一种特殊形式。
我们可以通过key值去获取对应的数据,比起索引的方式会快很多,也会安全很多。
我们直接来看个例子
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);一上来你就看到了use,为什么呢?因为hash map相对于vector和String来说,用的不是很多。所以并没有放到prelude也就是不会预加载。所以需要我们手动从标准库中引入。
创建hashmap的方式也是通过关联函数new ,它没有macro也就是宏(语法糖),因为用得少(莫得面子)。

hash map的类型也是一个泛型HashMap,其中K表示key的类型,而V自然就是value的类型。
泛型都在这了,那自然hash map里所有的item的key值类型都得一样,value也是得相同类型。
和vector类似,这里需要指定key和value的类型,或者接下来插入一个key-value数据,这样就会自动腿断了。
insert是hash map的方法,传入两个参数,第一个是key值,第二个是value。
获取value#
我们再来看个例子
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).copied().unwrap_or(0);
}调用hash map的get这个method,传入一个之前定义的key的类型的引用,我们来看下源码

可以看到返回的是Option<&V>的枚举,类型则是我们定义的value的类型。
然后又调用了一个copied的方法,这是干啥的呢?我们来看下源码

这是一个Option的method,可以看到如果值有效的时候会返回value的所有权,而不是引用。当然None还是一样。
然后又调用了unwrap_or的method,我们再来看下源码

一眼顶真(误),原来是空值保护,毕竟你这个key-value之前并没有插入过,而且这玩意儿只能是runtime的时候才能发现,也就是说编译器无法发现这种问题,所以这里自然只能交由开发者自己去处理。

最后拿到的值也是符合预期的。
遍历hash map#
和vector类似,都得是引用,我们来看下例子
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);
}
}
这里是可以解构的,所以可以推测可能存储的方式是以元组的方式存储的。

所有权#
因为你要把数据放到map里,所以之前的数据还在不在,这是个问题。
我们先来看下一个例子

标量类型就不说了,它们都实现了copy这个trait,直接就在stack上复制一个相同的值。
而String是一个数据存储在heap上的类型,这里如果把所有权传入到hash map中,会直接让原来的失效。
说到这,我们之前漏了vector的所有权,我们来试下

可以看到是一样的,如果所有权交由vector或者hash map,那么就会转移所有权,原来的变量就失效了。
另外,你可以传入引用到里面去,这样原来就不会失效。但是你得确保值的有效,不然会导致map里的也有问题。这时可以用上lifetimes也就是生命周期。
更新#
在map中,一个·key只能对应一个value,相信大家都知道了,这点就不多说了。
我们先来看第一种场景:原来已经有值了,现在准备覆盖[5]
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);
}
看来和别的语言一样。
然后我们再来看第二种场景:已经有值了,那就忽略[6]
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);
}
可以看到我们使用了entry和 or_insert这俩个method,
其中entry的作用是判断这个key是否存在,它会返回一个Entry的枚举,表示这个值存在与否。
而or_insert自然是用来插入默认值的,它是这个Entry的method

可以看到Occupied占领也就是已有值的时候返回这个值,而没有的时候插入默认值。最后都会返回一个引用值

来看下第三种场景:根据旧数据来更新[7]
来看下例子
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);
}split_whitespace方法作用是根据空格space切割字符串。
*这个是解引用(dereference),之后会说到,这里就暂时这样,只要知道这个可以更新数据就好。
哈希函数#
HashMap使用的是SipHash这一hashing function。它可以抵御DOS(Denial of Service)攻击。
hash map并不是速度最快的哈希算法,但它安全。
如果你嫌弃它太慢,你可以自己切换成其他的hash函数,它们都是实现了一个BuildHasher的trait。
如果你想自己写一个,你可以通过去实现这个BuildHasher的trait来快速实现。
当然,自己写还不如直接去找一个,下面这个链接有其它rust用户实现的hashing算法,你可以找一个。
crates.io: Rust Package Registrycrates.io/
总结#
今天我们学了hash map,知道如何插入、读取以及更新。
最后,如果觉得这篇文章对你有帮助的话,请务必点个赞~
参考#
- ^rust-hash-maps https://doc.rust-lang.org/book/ch08-03-hash-maps.html#storing-keys-with-associated-values-in-hash-maps
- ^rust-access-value-in-hash-map https://doc.rust-lang.org/book/ch08-03-hash-maps.html#accessing-values-in-a-hash-map
- ^hash-map-ownership https://doc.rust-lang.org/book/ch08-03-hash-maps.html#hash-maps-and-ownership
- ^rust-update-hash-map https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-hash-map
- ^rust-updating-hash-map-overwrite https://doc.rust-lang.org/book/ch08-03-hash-maps.html#overwriting-a-value
- ^rust-updating-hash-map-2 https://doc.rust-lang.org/book/ch08-03-hash-maps.html#adding-a-key-and-value-only-if-a-key-isnt-present
- ^rust-udating-hash-map-3 https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value
- ^rust-hashing-functions https://doc.rust-lang.org/book/ch08-03-hash-maps.html#hashing-functions
发布于 2022-12-11 12:26・IP 属地广东
