rust學習九.3-集合之雜湊對映表

正在战斗中發表於2024-11-18

這裡介紹的雜湊對映表(HashMap)並非是java那樣的萬用表,限制很大。

不過,話說回來,rust應該是有類似java那樣的對映表,不過不是這個雜湊對映表。現在先談論雜湊對映表吧。

一、構成和定義

HashMap 是最不常用的,所以並沒有被 prelude 自動引用。標準庫中對 HashMap 的支援也相對較少,例如,並沒有內建的構建宏。

必須在程式碼前use下:

use std::collections::HashMap;

透過有關工具,我們知道雜湊對映表內部程式碼是這樣的:

和其它語言相比,沒有什麼特別的,顯著的不同,都是包含陣列(類似陣列之類的)、屬性、一些公共的方法。

rust的雜湊對映表具有許許多多的的方法/屬性,例如:

以上只是部分。

可見,rust的建立者們還是挺費心的,並保持了他們的一貫風格:為開發者們提供了有點盡善盡美的各種實現。

java的實現某種程度看起來更加得簡潔。

rust提供了那麼多的實現,部分原因也是因為它獨有複雜的所有轉問題導致的,rust力圖讓程式設計師不要在寫程式碼的時候想著雜湊表的值所有權的問題

二、基本操作

事實上,如前文所言,rust為雜湊對映表提供了居多的方法,所以這裡有僅僅是列出比較常用的一些,又或者就是作者例文中給出的。

插入

insert

很重要的一點:鍵型別必須一致,值型別必須一致,否則會報錯:

一個很貼心的提示: expected &str ,found Vec...

如果硬要插入不同的,也是間接的,例如插入Option類的值,但那樣讓一個值變得複雜了。

訪問單個成員

get(&o)

get_mut(&o)

還有許多。

注意,rust的建立者們很喜歡使用Option來包裝返回值。也許Option應該改名為萬用塑膠袋。

刪除

remove

修改

鍵是不可修改的,只能修改值。所以所謂的修改就是覆蓋值,這個和java等語言的操作是一樣的,插入相同的鍵及其值會自然覆蓋已有的,從而達到修改值得目的。

簡單一點,就是再次insert

列印

如果是簡單得,那麼println!("{:?}",xxx),或者再稍微複雜一些: println!("{:#?}",xxx).

遍歷

有常見的集中方式:

遍歷鍵集合、便利值集合、迭代器、 (K,V) IN ()

其它

清空.-clear

三、所有權

即:建立一個變數v,然後作為一個值放入雜湊表,那麼v值的所有權歸歸於誰?

凡是複雜的型別(或者說複合型別)都有這樣的問題。 我們只需要記住幾個基本點即可。

根據第四章的重要規則:

  1. 一個值(不是變數)一定要有所有者
  2. 而且任意時刻,一個值只能有一個所有者
  3. 所有者離開作用域後,持有的值會被立刻釋放

如果不做特殊處理,所有權就會發生轉移-這是毋庸置疑的。

所以,insert之後,原來的值得所有權會發生轉移...

當然,如果你的鍵或者值屬於棧型別的,就不會有這個問題,因為所有權這個事情從來都屬於堆型別。

四、示例

/**
 * map 也就是大部分語言中具有的型別,中文稱為對映表
 * 內部的實現並沒有什麼本質的區別:一個陣列或者類似陣列的東西,加上一堆方法。
 * 根據具體的情況,可以細分為許多子型別的對映表,就好像java那樣
 * 
 * 一、雜湊對映表的定義/建立
 *    1.所有鍵的型別必須一致,所有值的型別必須是一致,這和java是大不一樣的。  rust應該也有和java對應的,但應該不是雜湊對映表
 * 
 * 二、對映表的基本操作
 * 
 * 三、對映表的一些特殊操作
 * 
 * 四、對映表的一些特殊操作
 * 
 * 五、對映表的小結
 * 
 */
use std::collections::HashMap;
use std::any::type_name;
fn print_type_of<T>(_: &T) {
    println!("The type is: {}", type_name::<T>());
}
fn main(){

    let mut family=HashMap::new();
    family.insert(String::from("父親"),"lzf");
    family.insert(String::from("母親"),"hxl");
    family.insert(String::from("女兒"),"lml");
    family.insert(String::from("男兒"),"lql");

    println!("{:?}",&family);

    let mut  sun=Vec::new();sun.push("阿大");sun.push("阿二");
    //family.insert(String::from("孫輩"),sun);  //這樣是會報錯的
    //println!("{:?}",&family);
    println!("{:#?}",&family); //更好的格式

    let mut scores=HashMap::new();
    let g_bad:String=String::from("");
    let g_mid:String=String::from("");
    let g_good:String=String::from("");
    let g_excellent:String=String::from("");
    let g_perfect:String=String::from("完美");

    scores.insert(String::from("101"),g_bad);   //g_bad被奪取所有權了
    scores.insert(String::from("102"),g_mid);
    scores.insert(String::from("103"),g_excellent);
    scores.insert(String::from("104"),g_good);
    scores.insert(String::from("105"),g_perfect);
    println!("{:#?}",&scores);

    println!("get方法會返回一個不可修改的物件");
    let s103=scores.get("103"); //返回的是一個Option
    println!("103的成績:{}",s103.unwrap());
    //s103=Some(&String::from("說不清"));  // 這是不可修改,會報錯
    let s106=scores.get("106"); //訪問一個不存在,會返回None
    println!("106的成績:{}",s106.unwrap_or(&String::from("不存在")));

    let  mut_s104=scores.get_mut("104");
    print_type_of(&mut_s104);
    println!("現在104的成績:{}",mut_s104.unwrap());
    
    //println!("{}",g_bad); //g_bad被奪取所有權了,所以這裡會報錯的    
    print_hm_use_forkv(&scores);
    print_hm_use_forvalues(&scores);
}

fn print_hm_use_forkv(hm:&HashMap<String,String>){
    for (k,v) in hm{
        println!("{}:{}",k,v);
    }
}

fn print_hm_use_forvalues(hm:&HashMap<String,String>){
    for value in hm.values() {
        println!("{}",value);
    }
}

五、小結

  1. 雖然和大部分語言類似,但是還是有不小侷限性。用起來不是那麼友好。這都是拜所有權所賜
  2. 整體上,還是比較方便。但需要特別注意所有權問題
  3. rust提供了足夠豐富的函式來處理各種需要

相關文章