【Rust 基础入门】(09) | 数组、Hashmap

  • 0xE
  • 发布于 3天前
  • 阅读 273

本文介绍了 Rust 中的数组(静态数组和动态数组)和 HashMap 的基本概念、创建方法、操作及其内部机制,包括数组的固定长度与动态扩展、HashMap 的键值对存储与扩容策略。

静态数组

数组是由多个相同类型的元素组合而成的集合。在 Rust 中,数组主要分为两类:

  • 静态数组 (array):分配在栈上,长度固定,访问速度快。
  • 动态数组 (Vector):分配在堆上,长度可变,但操作会带来额外的性能开销。

静态数组的特点类似于一个固定大小的书架,一旦确定其大小,就无法再改变。相比之下,动态数组更像是一个可以随时调整大小的抽屉,可以根据需要扩展或缩小。

创建静态数组

// 由编译器推断类型
let a = [10, 20, 30, 40, 50];

// 显式指定类型和长度
let b: [i32; 5] = [10, 20, 30, 40, 50];

// 创建包含相同元素的数组
let c = [7; 5];  // c = [7, 7, 7, 7, 7]

访问与操作

fn main() {
    // 静态数组:长度固定,存储在栈上
    let a = [5u8; 5]; // a = [5, 5, 5, 5, 5]

    // 非基础类型的数组需要使用 `std::array::from_fn` 来初始化
    let b: [String; 3] = std::array::from_fn(|_| "hello".to_string()); 
    // b = ["hello", "hello", "hello"]

    // 数组的元素访问
    let c: [u8; 5] = [15, 25, 35, 45, 55];
    let first = c[0];  // first = 15
    let second = c[1]; // second = 25

    // 访问数组越界元素(会导致编译错误)
    // let none_element = c[100];

    // 二维数组:每个元素都是一个 `[u8; 5]` 类型的数组
    let arrays: [[u8; 5]; 2] = [a, c]; 
}

动态数组(Vec<T>)

动态数组 Vec&lt;T> 是 Rust 中可变长度的集合,允许在运行时动态调整大小。与 String 不同,Vec&lt;T> 是通用的,可存储任意类型的元素。

创建动态数组

// 显式声明类型
let v1: Vec&lt;i32> = Vec::new();

// 让编译器推断类型。
let mut v2 = Vec::new();
v2.push(10);

// 使用 vec! 宏创建并初始化
let v3 = vec![10, 20, 30];

// 使用 [初始值;长度] 来创建数组
let v4 = vec![1; 3];  // v4 = [1, 1, 1]

// 使用from语法创建数组
let v5 = Vec::from([1, 1, 1]);

访问与修改

fn main() {
    let mut v = vec![1, 2, 3, 4, 5];

    // 访问元素
    let first: &i32 = &v[0];
    println!("第一个元素是 {}", first);

    // 通过 get 方法安全访问
    if let Some(last) = v.get(4) {
        println!("最后一个元素是 {}", last);
    }

    // 修改元素
    for i in &mut v {
        *i *= 2;
    }
    println!("修改后的 v: {:?}", v); // [2, 4, 6, 8, 10]

    // 删除元素
    v.pop();
    println!("删除最后一个元素后: {:?}", v); // [2, 4, 6, 8]

    v.remove(1);
    println!("删除索引 1 处的元素后: {:?}", v); // [2, 6, 8]
}

动态扩容机制

fn main() {
    let mut v = vec![1, 2, 3, 4];

    println!("初始容量: {}", v.capacity());
    println!("初始 v[0] 的地址: {:p}", &v[0]);

    v.push(5);
    println!("添加一个元素后,容量: {}", v.capacity());
    println!("添加后 v[0] 的地址: {:p}", &v[0]);

    v.push(6);
    println!("再次添加元素后,容量: {}", v.capacity());
    println!("再次添加后 v[0] 的地址: {:p}", &v[0]);
}

初始时 Vec 的容量可能是 4,存储在堆内存中,而栈上存储着指向堆的指针。当 push(5) 触发扩容时,Vec 会申请更大的空间(通常是当前容量的 2 倍),并将数据复制到新的地址。


HashMap 集合

HashMap 是键值对(key-value)存储结构,可以通过键快速检索值。Rust 使用散列函数计算键的位置,从而提高查找效率。

创建 HashMap

// 由于 HashMap 并没有包含在 Rust 的 prelude 库中,所以需要手动引入
use std::collections::HashMap;

fn main() {
    // 创建一个HashMap,用于存储学生姓名和他们的年龄
    let mut student_ages = HashMap::new();
    student_ages.insert("Alice", 20);
    student_ages.insert("Bob", 19);
    student_ages.insert("Charlie", 21);

    // 创建一个指定容量的 HashMap,用于存储学生姓名和他们的身高
    // 这样可以预先分配内存,减少后续插入时的内存分配开销
    let mut student_heights = HashMap::with_capacity(4);
    student_heights.insert("Alice", 165);
    student_heights.insert("Bob", 180);
    student_heights.insert("Charlie", 175);
    student_heights.insert("David", 170);

    // 打印 HashMap 的内容
    println!("学生年龄: {:?}", student_ages);
    println!("学生身高: {:?}", student_heights);
}

HashMap 操作

use std::collections::HashMap;

fn main() {
    // 创建一个包含用户姓名和积分的列表
    let user_list = vec![
        ("Alice", 300),
        ("Bob", 250),
        ("Eve", 150),
        ("Mallory", 50),
    ];

    // 将列表转换为 HashMap
    let mut user_map: HashMap&lt;&str, i32> = user_list.into_iter().collect();
    println!("用户积分: {:?}", user_map);

    // 获取元素
    let alice_points = user_map["Alice"];
    println!("Alice 的积分: {}", alice_points);

    // 使用 get 方法安全获取元素
    let alice_points = user_map.get("Alice");
    println!("Alice 的积分: {:?}", alice_points);

    // 尝试获取不存在的键
    let trent_points = user_map.get("Trent");
    println!("Trent 的积分: {:?}", trent_points);

    // 覆盖已有的值,insert 会返回旧值
    let old = user_map.insert("Alice", 400); // 更新 Alice 的积分
    assert_eq!(old, Some(300)); // 确认旧值是 300

    // 使用 entry 方法插入或获取值
    let v = user_map.entry("Trent").or_insert(100); // Trent 不存在,插入 100
    assert_eq!(*v, 100);

    let v = user_map.entry("Trent").or_insert(200); // Trent 已存在,不会修改
    assert_eq!(*v, 100);

    // 打印更新后的 HashMap
    println!("更新后的用户积分: {:?}", user_map);
}

HashMap 提供 entry().or_insert() 方法,允许在键不存在时插入默认值,而不会覆盖已有值。例如,Trent 不存在时,or_insert(100) 会插入 100,但后续 or_insert(200) 不会修改已存在的值。

  • 原创
  • 学分: 5
  • 分类: Rust
  • 标签:
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
0xE
0xE
0x59f6...a17e
17年进入币圈,Web3 开发者。刨根问底探链上真相,品味坎坷悟 Web3 人生。有工作机会可加v:__0xE__