乐趣区

关于rust:rust学习在哈希map中存储具有关联值的键

与向量和 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 循环完结时超出范围,因而所有这些更改都是平安的,并且借用规定容许。

退出移动版