HashMap
- 键值对的形式存储数据,一个键
(key)
,对用一个(value)
- Hash 函数:决定如何在内存中存放
K
和V
- 适用场景:通过 K (任何类型)来寻找数据,而不是通过索引。
创建 HashMap
use std::collections::HashMap;
fn main() {
let mut h:HashMap<String, i32> = HashMap::new();
h.insert(String::from("age"), 20);
println!("{:?}", h);
}
HashMap
用的比较少,不在Prelude
中。- 标准库对其支持比较少,没有内置的宏来创建
HashMap
。 - 跟
Vector
一样,数据存储在Heap
上。 - HashMap 是同构的,一个 HashMap 中:
- 所有的 K 必须是同一种类型
- 所有的 V 必须是同一种类型
Collect 方法
在元素类型为 Tuple 的 Vector 上使用 Collect 方法,可以组建一个 HashMap:
要求 Tuple 有两个值:一个作为 K,一个作为 V
Collect 方法可以把数据整合成很多中集合类型,包括 HashMap
- 返回值需要显示指明类型
use std::collections::HashMap;
fn main() {
let items = vec![String::from("阿根廷"), String::from("西班牙")];
let intial_scores = vec![10, 50, 100];
let scopes: HashMap<_, _> = items.iter().zip(intial_scores.iter()).collect();
println!("{:?}", scopes);
}调用
iter()
方法来返回他们的遍历器,就可以创造一个元组的数组。zip
还有拉链的意思,把两个Vector
合成一个元组的数组,再调用collect
就可以创建一个HashMap
。items
和intial_scores
长度不一致则进行取短输出。HashMap
必须要指明,如果把类型去掉就会出错,因为collect
方法可以返回很多种不同的集合数据结构,如果不指明,Rust
就无法知道我们具体想要的类型,所以我们要声明类型。HashMap<_, _>
Key
和Value
使用了两个下划线,因为Rust
能够根据Vector
中的数据类型能推导出来HashMap
中的Key
和Value
的类型。
HashMap 和 所有权
对于实现了 Copy Trait 的类型(例如 i32),值会被复制到 HashMap 中。
对于拥有所有权的值(例如 String),值会被移动,所有权会转移给 HashMap。
use std::collections::HashMap;
fn main() {
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(field_name, field_value);
println!("{:?}", map);
}如果再输出这变量,就会发生错误println!("{},{}", field_name, field_value);
如果将值的引用插入到
HashMap
, 值本身不会移动。- 在
HashMap
有效的期间,被引用的值必须保持有效。
use std::collections::HashMap;
fn main() {
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(&field_name, &field_value);
println!("{:?}", map);
println!("{},{}", field_name, field_value);
}- 在
访问 HashMap 中的值
- get 方法
- 参数:K
- 返回:Option<&V>
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 12);
scores.insert(String::from("Yellow"), 30);
for (k, v) in &scores {
println!("{}: {}", k, v);
}
}
更新 HashMap<K, V>
- HashMap 大小可变
- 每个 K 同时只能对应一个 V
- 更新 HashMap 中的数据:
- K 已经存在,对应一个 V
- 替换现有的 V
- 保留现有的 V,忽略新的 V
- 合并现有的 V 和 新的 V
- K 不存在
- 添加一对 K , V
- K 已经存在,对应一个 V
替换现有的 V
如果像 HashMap 插入一对 KV,然后再插入同样 K , 但是不同的 V , 那么原来的 V 会被替换掉。
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 12);
scores.insert(String::from("Blue"), 30);
println!("{:?}", scores);
}
只在 K 不对应任何值的情况下,才插入 V
entry
方法:检查指定的 K 是否对应一个 V- 参数为 K
- 返回 enum Entry:代表值是否存在
Entry
的or_insert()
方法:- 返回:
- 如果 K 存在,返回到对应的 V 的一个可变引用
- 如果 K 不存在,将方法参数作为 K 的新值插进去,返回到这个值的可变引用
- 返回:
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 12);
let e = scores.entry(String::from("Yellow"));
println!("{:?}", e);
// Entry这个值里面有 VacantEntry 变体, Vacant就是空的意思,也就是 Yellow 这个值是不存在的,既然不存在 or_insert 就会把 50 插入到 HashMap 中,K 就是 Yellow V 就是 50
e.or_insert(50);
println!("{:?}", scores);
}
基于现有的 V 来更新未来的 V
use std::collections::HashMap;
fn main() {
let text = "a b c d";
let mut map = HashMap::new();
for word in text.split_ascii_whitespace() {
let count = map.entry(word).or_insert(0);
*count+=1;
}
println!("{:?}", map);
}
Hash 函数
默认的情况下,
HashMap
使用加密功能强大的Hash
函数,可以抵抗拒绝服务器的(DoS)攻击。- 不是可用的最快的
Hash
算法 - 但是具有更好安全性的优势
- 不是可用的最快的
如果感觉性能不够好,可以指定不同的 hasher 来切换到另一个函数。
- hasher 是实现 BuildHasher Trait 的类型