zoukankan      html  css  js  c++  java
  • Rust-HashMap储存键值对

    在此也介绍常用的集合类型:哈希 map (hasp map)。

    HashMap<K,V>类型储存了一个键类型K对应一个值类型V的映射。它通过一个哈希函数来实现映射,决定如何将键和值放入内存中。很多编程语言支持这种数据结构。

    新建一个HashMap

    可以使用new创建一个空的HashMap,并使用insert增加元素。

        let mut map = HashMap::new();
        map.insert(String::from("1"),10);
        map.insert(String::from("2"),20);
        //print all elements
        for item in map {
            println!("key is {}, value is {}", item.0, item.1)
        }

     注意必须首先use标准库中集合部分的HashMap。

    use std::collections::HashMap;

    在这三个常用集合中,HashMap是最不常用的,所以并没有被prelude自动引用。标准库中对HashMap的支持也相对较少,例如,并没有内建的构建宏。什么是宏?

    像vector一样,hashmap将它们的数据储存在堆上,这个HashMap的键类型是String而值类型是i32 。类似于vector,HashMap是同质的:所有键必须是相同类型,值也必须都是相同类型。

    另一个构建HaspMap的方法是使用一个元组的vector的collect方法,其中每个元组包含一个键值对。collect方法可以将数据收集进一系列的集合类型,包括HaspMap。例如,如果key和value初始数据分别在两个vector中,可以使用zip方法来创建一个元组vector,其中"1"和10是一对。接着使用collect方法将这个元组vector转换成一个HashMap:

        let keys = vec![String::from("1"), String::from("2")];
        let values = vec![10,20];
        let map2:HashMap<_,_> = keys.iter().zip(values.iter()).collect();
        //print all elements
        for item in map2 {
            println!("key is {}, value is {}", item.0, item.1)
        }

    这里HashMap<_,_>类型注解是必要的,因为可能collect为很多不同的数据,而除非显式指定,否则Rust无从得知你需要的类型。但是对于键和值的类型参数来说,可以使用下划线占位,而Rust能够根据vector中数据的类型推断出HashMap所包含的类型。

    HashMap和所有权

     对于像i32这样的实现了Copy trait的类型,其值可以copy进HashMap。对于像String这样拥有所有权的值,其值将被移动而HashMap会成为这些值的所有者。

        let name = String::from("name");
        let value = String::from("value");
        let mut map = HashMap::new();
        map.insert(name,value);
        //这里的name和value不再有效,如果使用他们会出现编译错误

    如果我们使用name,会出现编译错误:

    error[E0382]: borrow of moved value: `name`
       --> src/main.rs:223:34
        |
    218 |     let name = String::from("name");
        |         ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
    ...
    221 |     map.insert(name,value);
        |                ---- value moved here
    222 |     //这里的name和value不再有效,如果使用他们会出现编译错误
    223 |     println!("can use name? {}", name)
        |                                  ^^^^ value borrowed here after move

    当insert调用将name和value移动到HashMap中后,将不能使用这两个绑定。

    如果将值的引用插入HashMap,这些值本身将不会被移动进HashMap。但是这些引用指向的值必须至少在HashMap有效时也是有效的。请查看"Rust-生命周期与引用有效性"了解更多。

    访问HashMap中的值

    可以通过get方法并提供对应的键来从HashMap中获取值:

        let mut score = HashMap::new();
        score.insert(String::from("blue"), 10);
        score.insert(String::from("yellow"), 20);
        let teamName = String::from("blue");
        println!("{} scored {}", teamName, score.get(&teamName).unwrap());

    更新HashMap

    尽管键值对的数量是可以增长的,不过任何时候,每个键只能关联一个值。当我们想要改变哈希 map 中的数据时,必须决定如何处理一个键已经有值了的情况。可以选择完全无视旧值并用新值代替旧值。可以选择保留旧值而忽略新值,并只在键 没有 对应值时增加新值。或者可以结合新旧两值。

    覆盖一个值

    如果我们插入了一个键值对,接着用相同的键插入一个不同的值,与这个键相关联的旧值将被替换。

    只有键没有对应值时插入

    我们经常会检查某个特定的键是否有值,如果没有就插入一个值。为此哈希 map 有一个特有的 API,叫做entry,它获取我们想要检查的键作为参数。entry的返回值是一个枚举,Entry,它代表了可能存在也可能不存在的值。

    Entry的or_insert方法在键对应的值存在时就返回这个值的可变引用,如果不存在则将参数作为新值插入并返回新值的可变引用。

    根据旧值更新一个值

    另一个常见的HashMap的应用场景是找到一个键对应的值并根据旧的值更新它。例如,计数一些文本中每一个单词分别出现了多少次。我们使用HashMap以单词作为键并递增其值来记录我们遇到过几次这个单词。如果是第一次看到某个单词,就插入值0。

        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)

    这会打印出:

    {"hello": 1, "world": 2, "wonderful": 1}

    or_insert方法事实上会返回这个键的值的一个可变引用(&mut V)。这里我们将这个可变引用储存在count变量中,所以为了赋值必须首先使用星号(*)解引用count。这个可变引用在for循环的结尾离开作用域,这样所有这个改变都是安全的并符合借用规则。

  • 相关阅读:
    边推改革边上“保险” 央行“双降”两大亮点带来哪些变化
    今天走势将冲高回落后重新回归下跌周期
    小心!资本正在流出中国:国际收支表里被遗漏的-2547亿美元
    价格改革确立时间表和路线图 六大重点领域破题
    避免在办公室体重上升的三大良策
    别再说自己有多忙
    沪指可能展开一波3个交易日的调整
    专车新政博弈 垄断行业改革样本
    JS和CS互访【后台前台代码调用JavaScript变量以及JavaScript调用代码变量】
    net9:图片文件转换成二进制流存入SQL数据库,以及从数据库中读取二进制流输出文件
  • 原文地址:https://www.cnblogs.com/johnnyzhao/p/15328038.html
Copyright © 2011-2022 走看看