集合型別(Vec 與 HashMap)#

集合(Collection) 是可以儲存多個值的資料結構。 不同於固定大小的陣列,集合的長度可以在執行時動態增減,資料存放在 堆積(Heap) 上。

Rust 標準函式庫提供多種集合,其中最常用的是 Vec<T> (動態陣列)和 HashMap<K, V> (雜湊映射)。


1. Vec — 動態陣列#

Vec<T> 儲存一系列 相同型別 的值,依序排列,可以動態增減長度。

建立 Vec#

1
2
3
4
5
6
7
fn main() {
    // 方法一:空 Vec,需明確標注型別
    let v: Vec<i32> = Vec::new();

    // 方法二:用 vec! 巨集,型別自動推斷
    let v = vec![1, 2, 3, 4, 5];
}

新增元素#

1
2
3
4
5
6
7
fn main() {
    let mut v = Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);
    println!("{:?}", v); // [1, 2, 3]
}

Vec 必須宣告為 mut 才能修改。

存取元素#

兩種存取方式,差別在於越界時的行為:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
fn main() {
    let v = vec![10, 20, 30];

    // 方法一:索引(超出範圍會 panic)
    println!("{}", v[0]); // 10

    // 方法二:get(),回傳 Option<&T>,安全不 panic
    match v.get(2) {
        Some(val) => println!("第三個元素:{val}"), // 30
        None      => println!("索引超出範圍"),
    }

    println!("{:?}", v.get(10)); // None
}

推薦使用 get() 避免程式意外 panic。

迭代 Vec#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
fn main() {
    let v = vec![1, 2, 3];

    // 不可變迭代(借用)
    for n in &v {
        println!("{n}"); // n 的型別是 &i32
    }

    // 可變迭代(修改元素)
    let mut v = vec![1, 2, 3];
    for n in &mut v {
        *n *= 2; // 解參考後修改
    }
    println!("{:?}", v); // [2, 4, 6]
}

常用方法#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
fn main() {
    let mut v = vec![3, 1, 4, 1, 5, 9, 2, 6];

    println!("長度:{}", v.len());          // 8
    println!("是否為空:{}", v.is_empty()); // false

    v.push(7);                              // 尾端新增
    let last = v.pop();                     // 移除並回傳尾端元素,型別是 Option<T>
    println!("{:?}", last);                 // Some(7)

    v.sort();
    println!("{:?}", v); // [1, 1, 2, 3, 4, 5, 6, 9]

    println!("包含 5:{}", v.contains(&5)); // true
}

2. HashMap — 雜湊映射#

HashMap<K, V> 儲存 鍵值對(key-value pair) ,透過鍵快速查詢對應的值。

使用前需引入:

1
use std::collections::HashMap;

建立與插入#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();

    scores.insert(String::from("Alice"), 100);
    scores.insert(String::from("Bob"), 85);

    println!("{:?}", scores);
}

查詢#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert(String::from("Alice"), 100);
    scores.insert(String::from("Bob"), 85);

    // get() 回傳 Option<&V>
    let alice = scores.get("Alice");
    println!("{:?}", alice); // Some(100)

    // 直接用 [] 存取(鍵不存在會 panic)
    println!("{}", scores["Bob"]); // 85

    // 判斷鍵是否存在
    println!("{}", scores.contains_key("Carol")); // false
}

更新值#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert(String::from("Alice"), 100);

    // 直接覆蓋舊值
    scores.insert(String::from("Alice"), 110);
    println!("{:?}", scores.get("Alice")); // Some(110)
}

entry API — 不存在才插入#

entry() 是更新 HashMap 最慣用的方式,避免多餘的查詢:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert(String::from("Alice"), 100);

    // "Bob" 不存在,插入 0
    scores.entry(String::from("Bob")).or_insert(0);
    // "Alice" 已存在,不做任何事
    scores.entry(String::from("Alice")).or_insert(0);

    println!("{:?}", scores);
    // {"Alice": 100, "Bob": 0}
}

or_insert() 回傳該鍵對應值的 &mut V,可以直接修改。以下是統計單字出現次數的經典寫法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use std::collections::HashMap;

fn main() {
    let text = "hello world hello rust hello";
    let mut word_count = HashMap::new();

    for word in text.split_whitespace() {
        let count = word_count.entry(word).or_insert(0);
        *count += 1; // 解參考後累加
    }

    println!("{:?}", word_count);
    // {"hello": 3, "world": 1, "rust": 1}
}

迭代 HashMap#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert("Alice", 100);
    scores.insert("Bob", 85);
    scores.insert("Carol", 92);

    // 迭代順序不固定
    for (name, score) in &scores {
        println!("{name}: {score}");
    }
}

常用方法#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert("a", 1);
    map.insert("b", 2);

    println!("長度:{}", map.len());              // 2
    println!("含鍵 a:{}", map.contains_key("a")); // true

    map.remove("a");
    println!("移除後長度:{}", map.len());        // 1
}

3. Vec 與 HashMap 比較#

Vec<T>HashMap<K, V>
用途有序元素清單鍵值對查詢
存取方式索引(v[i]鍵(map[key]
平均存取速度O(1)O(1)
使用場景資料清單、堆疊、佇列計數、索引、快取
需要 use不需要use std::collections::HashMap

Reference#

https://doc.rust-lang.org/book/ch08-00-common-collections.html