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循環的結尾離開作用域,這樣所有這個改變都是安全的並符合借用規則。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM