Rust 數據結構:HashMap
- Rust 數據結構:HashMap
- 創建一個新的哈希映射
- HashMap::new()
- 將元組變成哈希表
- 訪問哈希映射中的值
- 哈希映射和所有權
- 更新哈希映射
- 重寫一個值
- 僅當鍵不存在時才添加鍵和值
- 基于舊值更新值
- 散列函數
Rust 數據結構:HashMap
類型 HashMap<K, V> 使用散列函數存儲 K 類型鍵到 V 類型值的映射。
創建一個新的哈希映射
HashMap::new()
創建空散列映射的一種方法是使用 new 和 insert 來添加元素。
use std::collections::HashMap;let mut scores = HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Yellow"), 50);
就像 vector 一樣,哈希映射將它們的數據存儲在堆上。它也是同構的:所有鍵必須具有相同的類型,所有值必須具有相同的類型。
將元組變成哈希表
use std::collections::HashMap;let vec = vec![("k1", "v1"), ("k2", "v2")];
let map: HashMap<_, _> = vec.into_iter().collect();
訪問哈希映射中的值
我們可以通過向 get 方法提供鍵值來從哈希映射中獲取值。
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).copied().unwrap_or(0);
在這里,score將為 10。get 方法返回一個 Option<&V>,如果該鍵在哈希映射中沒有值,get 將返回 None。這個程序通過調用 copied 來處理 Option,得到一個 Option<i32>,而不是一個 Option<&i32>,然后 unwrap_or 將 score 設置為 0,如果 scores 沒有對應鍵的條目。
我們可以用 for 循環迭代哈希映射中的每個鍵值對:
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}");}
哈希映射和所有權
對于實現 Copy trait 的類型,比如 i32,值被復制到哈希映射中。對于 String,這些值將被移動,哈希映射將成為這些值的所有者。
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 and field_value are invalid at this point, try using them and// see what compiler error you get!
在調用 insert 將變量 field_name 和 field_value 移動到哈希映射之后,我們就不能使用它們了。
如果我們將值的引用插入到哈希映射中,這些值不會被移動到哈希映射中。引用所指向的值必須至少在哈希映射有效的時間內有效。
更新哈希映射
盡管鍵和值對的數量是可增長的,但每個唯一鍵一次只能有一個與之關聯的值。
重寫一個值
如果我們在哈希映射中插入一個鍵和一個值,然后用不同的值插入相同的鍵,那么與該鍵相關聯的值將被替換。
use std::collections::HashMap;let mut scores = HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Blue"), 25);println!("{scores:?}");
僅當鍵不存在時才添加鍵和值
通常檢查一個特定的鍵是否已經存在于哈希映射中并帶有值,然后采取以下操作:如果鍵確實存在于哈希映射中,則現有的值應保持原樣;如果鍵不存在,則插入它并為它插入一個值。
哈希映射為此有一個特殊的 API,稱為 entry,它將您想要檢查的鍵作為參數。entry 方法的返回值是一個名為 entry 的枚舉,它表示一個可能存在也可能不存在的值。
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:?}");
or_insert 方法被定義為:如果對應的鍵存在,則返回對該鍵值的可變引用,如果不存在,則將該參數作為該鍵的新值插入,并返回對新值的可變引用。這種技術比我們自己編寫邏輯要干凈得多,而且與借用檢查器配合得更好。
基于舊值更新值
哈希映射的另一個常見用例是查找鍵的值,然后根據舊值更新它。
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:?}");
split_whitespace 方法返回一個迭代器,該迭代器覆蓋文本中值的子片,這些子片由空格分隔。or_insert 方法返回一個對指定鍵值的可變引用(&mut V)。這里,我們將該可變引用存儲在 count 變量中,因此為了對該值賦值,必須首先使用 * 解引用。可變引用在 for 循環結束時將超出作用域,因此所有這些更改都是安全的,并且是借用規則允許的。
散列函數
默認情況下,HashMap 使用名為 SipHash 的哈希函數,該函數可以抵抗涉及哈希表的拒絕服務(DoS)攻擊。這不是最快的散列算法,但相對安全。
散列器是一種實現 BuildHasher trait 的類型,可以通過指定不同的哈希器來切換到另一個散列函數。