- Rust編程:入門、實(shí)戰(zhàn)與進(jìn)階
- 朱春雷
- 4744字
- 2021-04-30 12:37:23
2.4 容器類型
Rust標(biāo)準(zhǔn)庫std::collections提供了4種通用的容器類型,包含以下8種數(shù)據(jù)結(jié)構(gòu),如表2-2所示。
表2-2 Rust容器類型

下面對Rust編程中最常使用、后續(xù)實(shí)戰(zhàn)中會反復(fù)用到的Vec、VecDeque和HashMap進(jìn)行介紹。
2.4.1 Vec
Vec是一種動態(tài)可變長數(shù)組(簡稱動態(tài)數(shù)組),即在運(yùn)行時(shí)可增長或者縮短數(shù)組的長度。動態(tài)數(shù)組在內(nèi)存中開辟了一段連續(xù)內(nèi)存塊用于存儲元素,且只能存儲相同類型的元素。新加入的元素每次都會被添加到動態(tài)數(shù)組的尾部。動態(tài)數(shù)組根據(jù)添加順序?qū)?shù)據(jù)存儲為元素序列。序列中每個(gè)元素都分配有唯一的索引,元素的索引從0開始計(jì)數(shù)。
1. 動態(tài)數(shù)組的創(chuàng)建
創(chuàng)建動態(tài)數(shù)組有以下3種方式。
1)使用Vec::new函數(shù)創(chuàng)建空的動態(tài)數(shù)組,代碼如下:
let mut v: Vec<i32> = Vec::new();
2)使用Vec::with_capacity函數(shù)創(chuàng)建指定容量的動態(tài)數(shù)組,代碼如下:
let mut v: Vec<i32> = Vec::with_capacity(10);
動態(tài)數(shù)組的容量是指為存儲元素所分配的內(nèi)存空間量,而長度是動態(tài)數(shù)組中實(shí)際存儲的元素個(gè)數(shù)。動態(tài)數(shù)組的長度小于其容量時(shí),動態(tài)數(shù)組可以隨意增長,但動態(tài)數(shù)組的長度超過其容量時(shí)就需要重新分配更大的容量。比如,容量為10的動態(tài)數(shù)組在存儲10個(gè)以內(nèi)的元素時(shí),不會改變?nèi)萘俊.?dāng)動態(tài)數(shù)組的長度增加到11時(shí),就需要重新分配容量。這個(gè)分配過程會消耗一定的系統(tǒng)資源,因此應(yīng)該盡可能根據(jù)初始元素個(gè)數(shù)以及增長情況來指定合理的容量。
3)使用vec!宏在創(chuàng)建動態(tài)數(shù)組的同時(shí)進(jìn)行初始化,并且根據(jù)初始值自動推斷動態(tài)數(shù)組的元素類型。代碼如下所示,第1行代碼創(chuàng)建一個(gè)空的動態(tài)數(shù)組,由于沒有初始值,程序并不知道動態(tài)數(shù)組要存儲什么類型的元素,因此需要聲明元素類型。第2行代碼創(chuàng)建的動態(tài)數(shù)組的初始值是1、2、3,程序會根據(jù)初始值自動推斷元素類型是i32。第3行代碼創(chuàng)建的動態(tài)數(shù)組包含10個(gè)元素,元素的初始值全部是0。
1 let mut v: Vec<i32> = vec![]; 2 let mut v = vec![1, 2, 3]; 3 let mut v = vec![0; 10];
2. 動態(tài)數(shù)組的修改
修改動態(tài)數(shù)組的常見操作有添加元素、修改指定索引的元素值和刪除元素等。
1)使用push方法在動態(tài)數(shù)組的尾部添加新元素。代碼清單2-10中,第2行代碼中Vec::new函數(shù)創(chuàng)建了一個(gè)存儲i32類型元素的動態(tài)數(shù)組,第3~5行代碼中push方法將值添加到動態(tài)數(shù)組的尾部。
代碼清單2-10 添加元素
1 fn main() { 2 let mut v: Vec<i32> = Vec::new(); 3 v.push(1); 4 v.push(2); 5 v.push(3); 6 7 println!("v: {:?}", v); 8 } 9 10 // v: [1, 2, 3]
2)使用“實(shí)例名[索引]”語法為指定索引的元素重新賦值。代碼清單2-11中,第7行代碼將索引為1的元素值改為5,第8行代碼打印當(dāng)前動態(tài)數(shù)組的元素,此時(shí)對應(yīng)索引的元素值已被修改。
代碼清單2-11 修改指定索引的元素值
1 fn main() { 2 let mut v: Vec<i32> = Vec::new(); 3 v.push(1); 4 v.push(2); 5 v.push(3); 6 7 v[1] = 5; 8 println!("v: {:?}", v); 9 } 10 11 // v: [1, 5, 3]
3)刪除動態(tài)數(shù)組的元素有以下兩種方式。
① 使用pop方法刪除并返回動態(tài)數(shù)組的最后一個(gè)元素,如果數(shù)組為空則返回None。代碼清單2-12中,第2行代碼中Vec::with_capacity函數(shù)創(chuàng)建了一個(gè)指定容量為10、存儲i32類型元素的動態(tài)數(shù)組。第7行代碼中pop方法刪除最后一個(gè)元素,并返回打印的Option類型的值Some(3)。
代碼清單2-12 使用pop方法刪除元素
1 fn main() { 2 let mut v: Vec<i32> = Vec::with_capacity(10); 3 v.push(1); 4 v.push(2); 5 v.push(3); 6 7 println!("e: {:?}", v.pop()); 8 println!("v: {:?}", v); 9 } 10 11 // e: Some(3) 12 // v: [1, 2]
② 使用remove方法刪除并返回動態(tài)數(shù)組指定索引的元素,同時(shí)將其后面的所有元素向左移動一位。如果索引越界,將會導(dǎo)致程序錯(cuò)誤。代碼清單2-13中,第2行代碼vec!宏創(chuàng)建一個(gè)包含初始值為1、2、3的動態(tài)數(shù)組。第4行代碼中remove方法刪除索引為1的元素,并返回打印的i32類型的值2。
代碼清單2-13 使用remove方法刪除元素
1 fn main() { 2 let mut v = vec![1, 2, 3]; 3 4 println!("e: {}", v.remove(1)); 5 println!("v: {:?}", v); 6 } 7 8 // e: 2 9 // v: [1, 3]
3. 動態(tài)數(shù)組的訪問
訪問動態(tài)數(shù)組的元素有以下兩種方式。
1)使用“實(shí)例名[索引]”語法訪問指定索引的元素。代碼清單2-14中,第3行代碼訪問索引為2的元素,即動態(tài)數(shù)組的第3個(gè)元素。如果取消第4行的注釋,由于索引已越界,將會導(dǎo)致程序錯(cuò)誤。
代碼清單2-14 使用索引語法訪問元素
1 fn main() { 2 let v = vec![1, 2, 3]; 3 println!("e: {}", v[2]); 4 // println!("e: {}", v[10]); 5 } 6 7 // e: 3
2)使用get方法以索引作為參數(shù)訪問元素。代碼清單2-15中,第3行代碼訪問索引為2的元素,返回值類型是Option<&i32>。第4行代碼訪問索引為10的元素,由于索引已越界,將會返回None。關(guān)于Option<T>類型的知識點(diǎn)將會在第5章詳細(xì)介紹。
代碼清單2-15 使用get方法訪問元素
1 fn main() { 2 let v = vec![1, 2, 3]; 3 println!("e: {:?}", v.get(2)); 4 println!("e: {:?}", v.get(10)); 5 } 6 7 // e: Some(3) 8 // e: None
除了訪問動態(tài)數(shù)組指定索引的元素,我們還可以通過for循環(huán)遍歷動態(tài)數(shù)組的所有元素。代碼清單2-16中,第3~5行代碼使用for循環(huán)遍歷訪問動態(tài)數(shù)組的所有元素。第8~11行代碼使用for循環(huán)遍歷動態(tài)數(shù)組中每一個(gè)元素的可變引用,并使用解引用操作符“*”來追蹤和修改元素值。關(guān)于for循環(huán)、可變引用以及解引用操作符的知識點(diǎn),我們將會在后續(xù)章節(jié)詳細(xì)介紹。
代碼清單2-16 遍歷所有元素
1 fn main() { 2 let v = vec![10, 20, 30]; 3 for i in v { 4 print!("{} ", i); 5 } 6 7 let mut v = vec![10, 20, 30]; 8 for i in &mut v { 9 *i += 50; 10 print!("{} ", i); 11 } 12 } 13 14 // 10 20 30 60 70 80
2.4.2 VecDeque
雙端隊(duì)列是一種同時(shí)具有棧(先進(jìn)后出)和隊(duì)列(先進(jìn)先出)特征的數(shù)據(jù)結(jié)構(gòu),適用于只能在隊(duì)列兩端進(jìn)行添加或刪除元素操作的應(yīng)用場景。Rust使用VecDeque結(jié)構(gòu)體表示雙端隊(duì)列,它定義在標(biāo)準(zhǔn)庫的std::collections模塊中。使用VecDeque結(jié)構(gòu)體之前需要顯式導(dǎo)入std::collections::VecDeque。
1. VecDeque的創(chuàng)建
創(chuàng)建VecDeque有以下兩種方式。
1)使用VecDeque::new函數(shù)創(chuàng)建空的VecDeque,代碼如下:
let mut v: VecDeque<u32> = VecDeque::new();
2)使用VecDeque::with_capacity函數(shù)創(chuàng)建指定容量的VecDeque,代碼如下:
let mut v: VecDeque<u32> = VecDeque::with_capacity(10);
2. VecDeque的修改
修改VecDeque的常見操作有添加元素、修改指定索引的元素值和刪除元素等。
1)使用push_front方法在隊(duì)列的頭部添加新元素,使用push_back方法在隊(duì)列的尾部添加新元素。代碼清單2-17中,第4行代碼中VecDeque::new函數(shù)創(chuàng)建了一個(gè)存儲u32類型元素的VecDeque。第5~7行代碼中push_back方法將值添加到VecDeque的尾部。此時(shí),v中的元素是[1, 2, 3]。第8~9行代碼中push_front方法將值添加到VecDeque的頭部。此時(shí),v中的元素是[2, 1, 1, 2, 3]。
代碼清單2-17 添加元素
1 use std::collections::VecDeque; 2 3 fn main() { 4 let mut v: VecDeque<u32> = VecDeque::new(); 5 v.push_back(1); 6 v.push_back(2); 7 v.push_back(3); 8 v.push_front(1); 9 v.push_front(2); 10 11 println!("v: {:?}", v); 12 } 13 14 // v: [2, 1, 1, 2, 3]
2)使用“實(shí)例名[索引]”語法為指定索引的元素重新賦值。代碼清單2-18中,第9行代碼將索引為1的元素值改為5,第10行代碼打印當(dāng)前VecDeque的元素,此時(shí)對應(yīng)索引的元素值已被修改。
代碼清單2-18 修改指定索引的元素值
1 use std::collections::VecDeque; 2 3 fn main() { 4 let mut v: VecDeque<u32> = VecDeque::new(); 5 v.push_back(1); 6 v.push_back(2); 7 v.push_back(3); 8 9 v[1] = 5; 10 println!("v: {:?}", v); 11 } 12 13 // v: [1, 5, 3]
3)刪除VecDeque的元素有以下兩種方式。
① 使用pop_front方法刪除并返回隊(duì)列的頭部元素,使用pop_back方法刪除并返回隊(duì)列的尾部元素。代碼清單2-19中,使用第11行代碼中的pop_back方法刪除隊(duì)列尾部元素,并返回打印的Option類型的值Some(3)。此時(shí),v中的元素是[2, 1, 1, 2]。第12行代碼中的pop_front方法刪除隊(duì)列頭部元素,并返回打印的Option類型的值Some(2)。此時(shí),v中的元素是[1, 1, 2]。
代碼清單2-19 使用pop_front、pop_back方法刪除元素
1 use std::collections::VecDeque; 2 3 fn main() { 4 let mut v: VecDeque<u32> = VecDeque::new(); 5 v.push_back(1); 6 v.push_back(2); 7 v.push_back(3); 8 v.push_front(1); 9 v.push_front(2); 10 11 println!("e: {:?}", v.pop_back()); 12 println!("e: {:?}", v.pop_front()); 13 println!("v: {:?}", v); 14 } 15 16 // e: Some(3) 17 // e: Some(2) 18 // v: [1, 1, 2]
② 使用remove方法刪除并返回隊(duì)列指定索引的元素,同時(shí)將其后面的所有元素向左移動一位。如果索引越界,則返回None。代碼清單2-20中,第4行代碼中VecDeque::with_capacity函數(shù)創(chuàng)建一個(gè)指定容量為10、存儲u32類型元素的VecDeque。第9行代碼中remove方法刪除索引為1的元素,并返回打印的Option類型的值Some(2)。第10行代碼刪除索引為5的元素,由于索引已越界,返回None。
代碼清單2-20 使用remove方法刪除元素
1 use std::collections::VecDeque; 2 3 fn main() { 4 let mut v: VecDeque<u32> = VecDeque::with_capacity(10); 5 v.push_back(1); 6 v.push_back(2); 7 v.push_back(3); 8 9 println!("e: {:?}", v.remove(1)); 10 println!("e: {:?}", v.remove(5)); 11 println!("v: {:?}", v); 12 } 13 14 // e: Some(2) 15 // e: None 16 // v: [1, 3]
3. VecDeque的訪問
訪問VecDeque的元素有以下兩種方式。
1)使用“實(shí)例名[索引]”語法訪問指定索引的元素。代碼清單2-21中,第9行代碼訪問索引為0的元素,即VecDeque的第1個(gè)元素。如果取消第10行的注釋,由于索引越界,將會導(dǎo)致程序錯(cuò)誤。
代碼清單2-21 使用索引語法訪問元素
1 use std::collections::VecDeque; 2 3 fn main() { 4 let mut v: VecDeque<u32> = VecDeque::new(); 5 v.push_back(1); 6 v.push_back(2); 7 v.push_back(3); 8 9 println!("e: {}", v[0]); 10 // println!("e: {}", v[10]); 11 } 12 13 // e: 1
2)使用get方法以索引作為參數(shù)訪問元素。代碼清單2-22中,第9行代碼訪問索引為0的元素,返回值類型是Option<&i32>。第10行代碼訪問索引為10的元素,由于索引越界,將會返回None。
代碼清單2-22 使用get方法訪問元素
1 use std::collections::VecDeque; 2 3 fn main() { 4 let mut v: VecDeque<u32> = VecDeque::new(); 5 v.push_back(1); 6 v.push_back(2); 7 v.push_back(3); 8 9 println!("e: {:?}", v.get(0)); 10 println!("e: {:?}", v.get(10)); 11 } 12 13 // e: Some(1) 14 // e: None
2.4.3 HashMap
哈希表(HashMap)是基于哈希算法來存儲鍵-值對的集合,其中所有的鍵必須是同一類型,所有的值也必須是同一類型,不允許有重復(fù)的鍵,但允許不同的鍵有相同的值。Rust使用HashMap結(jié)構(gòu)體表示哈希表,它定義在標(biāo)準(zhǔn)庫的std::collections模塊中。使用HashMap結(jié)構(gòu)體之前需要顯式導(dǎo)入std::collections::HashMap。
1. HashMap的創(chuàng)建
創(chuàng)建HashMap有以下兩種方式。
1)使用HashMap::new函數(shù)創(chuàng)建空的HashMap,代碼如下:
let mut map: HashMap<&str, i32> = HashMap::new();
2)使用HashMap::with_capacity函數(shù)創(chuàng)建指定容量的HashMap,代碼如下:
let mut map: HashMap<&str, i32> = HashMap::with_capacity(10);
2. HashMap的修改
由于HashMap中元素是鍵-值對的特殊性,要修改HashMap就必須考慮鍵已經(jīng)有值的情況。修改HashMap的常見操作有插入/更新鍵-值對、只在鍵沒有對應(yīng)值時(shí)插入鍵-值對、以新舊兩值的計(jì)算結(jié)果來更新鍵-值對和刪除鍵-值對。
1)使用insert方法在HashMap中插入或更新鍵-值對。如果鍵不存在,執(zhí)行插入操作并返回None。如果鍵已存在,執(zhí)行更新操作,將對應(yīng)鍵的值更新并返回舊值。
代碼清單2-23中,第4行代碼中HashMap::new函數(shù)創(chuàng)建一個(gè)空的HashMap,可存儲由&str類型的鍵與i32類型的值組成的鍵-值對。第6行代碼由于鍵zhangsan不存在,insert方法執(zhí)行插入操作,返回None。第12行代碼由于鍵zhangsan已經(jīng)存在,insert方法執(zhí)行更新操作,鍵值由97變成79,返回Some(97)。
代碼清單2-23 使用insert方法插入/更新鍵-值對
1 use std::collections::HashMap; 2 3 fn main() { 4 let mut map: HashMap<&str, i32> = HashMap::new(); 5 6 let zhangsan1 = map.insert("zhangsan", 97); 7 map.insert("lisi", 86); 8 9 println!("{:?}", zhangsan1); 10 println!("{:?}", map); 11 12 let zhangsan2 = map.insert("zhangsan", 79); 13 println!("{:?}", zhangsan2); 14 println!("{:?}", map); 15 } 16 17 // None 18 // {"lisi": 86, "zhangsan": 97} 19 // Some(97) 20 // {"lisi": 86, "zhangsan": 79}
2)使用entry和or_insert方法檢查鍵是否有對應(yīng)值,沒有對應(yīng)值就插入鍵-值對,已有對應(yīng)值則不執(zhí)行任何操作。entry方法以鍵為參數(shù),返回值是一個(gè)枚舉類型Entry。Entry類型的or_insert方法以值為參數(shù),在鍵有對應(yīng)值時(shí)不執(zhí)行任何操作。在鍵沒有對應(yīng)值時(shí),將鍵與值組成鍵-值對插入HashMap。
代碼清單2-24中,第6行代碼中entry方法會檢查鍵zhangsan是否有對應(yīng)值,沒有對應(yīng)值就插入該鍵-值對。第10行代碼中entry方法會再次檢查鍵zhangsan是否有對應(yīng)值,發(fā)現(xiàn)鍵zhangsan已有對應(yīng)值97,那就不執(zhí)行任何操作直接返回這個(gè)值的類型Entry,因此鍵zhangsan的對應(yīng)值不變。
代碼清單2-24 使用entry方法插入鍵-值對
1 use std::collections::HashMap; 2 3 fn main() { 4 let mut map: HashMap<&str, i32> = HashMap::new(); 5 6 map.entry("zhangsan").or_insert(97); 7 map.entry("lisi").or_insert(86); 8 println!("{:?}", map); 9 10 map.entry("zhangsan").or_insert(79); 11 println!("{:?}", map); 12 } 13 14 // {"lisi": 86, "zhangsan": 97} 15 // {"lisi": 86, "zhangsan": 97}
3)以新舊兩值的計(jì)算結(jié)果來更新鍵-值對是指找到一個(gè)鍵對應(yīng)值,結(jié)合新舊兩值進(jìn)行某些計(jì)算處理,以計(jì)算結(jié)果來更新鍵對應(yīng)值。比如,老師發(fā)現(xiàn)本次考試試卷上出現(xiàn)了一道錯(cuò)題,決定為所有學(xué)生的分?jǐn)?shù)都加上2分,那么就可以將每個(gè)學(xué)生的名字作為鍵,將對應(yīng)分?jǐn)?shù)加上2。
代碼清單2-25中,第11行代碼中iter_mut方法會返回由HashMap中所有鍵-值對的可變引用組成的迭代器,通過for循環(huán)遍歷迭代器。由于只針對值做處理,因此只取&mut i32類型的val,通過解引用操作符“*”對val進(jìn)行賦值操作。關(guān)于迭代器、for循環(huán)、可變引用以及解引用操作符的知識點(diǎn),我們將會在后續(xù)章節(jié)詳細(xì)介紹。
代碼清單2-25 修改HashMap元素
1 use std::collections::HashMap; 2 3 fn main() { 4 let mut map: HashMap<&str, i32> = HashMap::new(); 5 6 map.insert("zhangsan", 97); 7 map.insert("lisi", 86); 8 map.insert("wangwu", 55); 9 println!("{:?}", map); 10 11 for (_, val) in map.iter_mut() { 12 *val += 2; 13 } 14 println!("{:?}", map); 15 } 16 17 // {"zhangsan": 97, "lisi": 86, "wangwu": 55} 18 // {"zhangsan": 99, "lisi": 88, "wangwu": 57}
4)使用remove方法刪除并返回指定的鍵-值對,如果不存在就返回None。代碼清單2-26中,第11行代碼中的remove方法刪除鍵wangwu的對應(yīng)值,并將值返回,打印Option類型的值Some(55)。
代碼清單2-26 使用remove方法刪除鍵-值對
1 use std::collections::HashMap; 2 3 fn main() { 4 let mut map: HashMap<&str, i32> = HashMap::new(); 5 6 map.insert("zhangsan", 97); 7 map.insert("lisi", 86); 8 map.insert("wangwu", 55); 9 println!("{:?}", map); 10 11 let result = map.remove("wangwu"); 12 println!("{:?}", map); 13 println!("{:?}", result); 14 } 15 16 // {"wangwu": 55, "lisi": 86, "zhangsan": 97} 17 // {"lisi": 86, "zhangsan": 97} 18 // Some(55)
3. HashMap的訪問
訪問HashMap中指定的鍵-值對有以下兩種方式。
1)使用“實(shí)例名[鍵]”語法訪問指定的鍵-值對。如果鍵不存在,將會導(dǎo)致程序錯(cuò)誤。代碼清單2-27中,第7行代碼訪問鍵zhangsan的對應(yīng)值,由于鍵zhangsan存在,可以正常訪問到對應(yīng)值。如果取消第8行的注釋,由于鍵wangwu不存在,將會導(dǎo)致程序錯(cuò)誤。
代碼清單2-27 使用“實(shí)例名[鍵]”語法訪問鍵-值對
1 use std::collections::HashMap; 2 3 fn main() { 4 let mut map: HashMap<&str, i32> = HashMap::new(); 5 map.insert("zhangsan", 97); 6 7 println!("zhangsan: {}", map["zhangsan"]); 8 // println!("wangwu: {}", map["wangwu"]); 9 } 10 11 // zhangsan: 97
2)使用get方法以鍵為參數(shù)訪問指定的鍵-值對。代碼清單2-28中,第7行代碼訪問鍵zhangsan的對應(yīng)值,返回值類型是Option<&i32>。第8行代碼訪問鍵wangwu的對應(yīng)值,由于鍵wangwu不存在,將會返回None。
代碼清單2-28 使用get方法為參數(shù)訪問指定的鍵-值對
1 use std::collections::HashMap; 2 3 fn main() { 4 let mut map: HashMap<&str, i32> = HashMap::new(); 5 map.insert("zhangsan", 97); 6 7 println!("zhangsan: {:?}", map.get("zhangsan")); 8 println!("wangwu: {:?}", map.get("wangwu")); 9 } 10 11 // zhangsan: Some(97) 12 // wangwu: None
- Java程序設(shè)計(jì)(慕課版)
- Reporting with Visual Studio and Crystal Reports
- Vue.js 3.x從入門到精通(視頻教學(xué)版)
- Java Web及其框架技術(shù)
- Programming ArcGIS 10.1 with Python Cookbook
- Python Game Programming By Example
- C/C++常用算法手冊(第3版)
- Learning SciPy for Numerical and Scientific Computing(Second Edition)
- Learning FuelPHP for Effective PHP Development
- Canvas Cookbook
- Python大規(guī)模機(jī)器學(xué)習(xí)
- 數(shù)據(jù)結(jié)構(gòu):Python語言描述
- Python Social Media Analytics
- 快樂編程:青少年思維訓(xùn)練
- Python自動化運(yùn)維:技術(shù)與最佳實(shí)踐