Skip to main content

HashMap

  • 键值对的形式存储数据,一个键(key),对用一个(value)
  • Hash 函数:决定如何在内存中存放 KV
  • 适用场景:通过 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

  • itemsintial_scores 长度不一致则进行取短输出。

  • HashMap 必须要指明,如果把类型去掉就会出错,因为 collect 方法可以返回很多种不同的集合数据结构,如果不指明,Rust 就无法知道我们具体想要的类型,所以我们要声明类型。 HashMap<_, _> KeyValue 使用了两个下划线,因为 Rust 能够根据 Vector 中的数据类型能推导出来 HashMap 中的 KeyValue 的类型。

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

替换现有的 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:代表值是否存在
  • Entryor_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 的类型