【百例RUST - 011】简单键值对

【百例RUST - 011】简单键值对

第一章 创建键值对

第01节 方式一

案例代码 直接创建

rust 复制代码
// 需要导入 HashMap 的包
use std::collections::HashMap;

fn main(){
    // 创建一个空的 HashMap 
    let mut scores: HashMap<String, i32> = HashMap::new();
    
    // 添加数据
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Red"), 20);

    // 输出结果
    println!("{:?}", scores);
}

// {"Blue": 10, "Red": 20}

第02节 方式二

案例代码 将键的容器 和 值的容器 进行组合

rust 复制代码
// 需要导入 HashMap 的包
use std::collections::HashMap;


fn main(){
    // 定义 Key 和 Value 的容器
    let keys: Vec<String> = vec![String::from("Blue"), String::from("Red")];
    let values: Vec<i32> = vec![10,20];

    // 将两个容器, 结合在一起, 作为 键值对
    let scores: HashMap<_,_> = keys.iter().zip(values.iter()).collect();
    
    // 输出结果
    println!("{:?}", scores);
}

// {"Blue": 10, "Red": 20}

第二章 读取数据

第01节 直接读取

案例代码

rust 复制代码
// 需要导入 HashMap 的包
use std::collections::HashMap;

fn main(){
    // 创建一个空的 HashMap 
    let mut scores: HashMap<String, i32> = HashMap::new();
    
    // 添加数据
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Red"), 20);

    // 定义需要获取的键
    let target_key = String::from("Blue");
    // 读取数据
    let result: Option<&i32> = scores.get(&target_key);
    // 结果进行匹配
    match result {
        None => println!("none"),
        Some(x) => println!("x = {}", x),
    }
}

// x = 10

第02节 遍历操作

案例代码

rust 复制代码
// 需要导入 HashMap 的包
use std::collections::HashMap;

fn main(){
    // 创建一个空的 HashMap 
    let mut scores: HashMap<String, i32> = HashMap::new();
    
    // 添加数据
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Red"), 20);

    // 遍历数据
    for(key, value) in &scores{
        println!("{}, {}" , key, value);
    }
}

// Blue, 10
// Red, 20

需要注意的是:遍历出现的结果顺序是不固定的

第三章 插入数据

第01节 直接插入

案例代码

rust 复制代码
// 需要导入 HashMap 的包
use std::collections::HashMap;

fn main(){
    // 创建一个空的 HashMap 
    let mut scores: HashMap<String, i32> = HashMap::new();
    
    // 添加数据
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Red"), 20);
    scores.insert(String::from("Yellow"), 30);
    
    println!("{:?}", scores);
    // 直接插入数据
    scores.insert(String::from("Black"), 40);
    println!("{:?}", scores);

    // 如果存在键, 则替换值
    scores.insert(String::from("Red"), 50);
    println!("{:?}", scores);
}

// {"Blue": 10, "Red": 20, "Yellow": 30}
// {"Yellow": 30, "Blue": 10, "Red": 20, "Black": 40}
// {"Yellow": 30, "Blue": 10, "Red": 50, "Black": 40}

第02节 不存在 则插入

当键不存在的情况下, 则插入数据,键存在,则不做插入的操作

rust 复制代码
// 需要导入 HashMap 的包
use std::collections::HashMap;

fn main(){
    // 创建一个空的 HashMap 
    let mut scores: HashMap<String, i32> = HashMap::new();
    
    // 添加数据
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Red"), 20);
    scores.insert(String::from("Yellow"), 30);
    
    println!("{:?}", scores);
    
    // 当键不存在的时候, 则进行插入的操作
    scores.entry(String::from("Red")).or_insert(66);
    println!("{:?}", scores);

    // 当键不存在的时候, 则进行插入的操作
    scores.entry(String::from("White")).or_insert(88);
    println!("{:?}", scores);
}

//  {"Blue": 10, "Red": 20, "Yellow": 30}
//  {"Blue": 10, "Red": 20, "Yellow": 30}
//  {"Yellow": 30, "Blue": 10, "Red": 20, "White": 88}

第03节 根据旧值 更新新值

案例代码

rust 复制代码
// 需要导入 HashMap 的包
use std::collections::HashMap;

fn main(){    
    // 根据旧值来更新一个值, 统计一个字符串当中, 单词出现的次数。
    let text = "hello world  my worldview world";
    // 将字符串, 存放到 HashMap 当中
    let mut map: HashMap<_, _> = HashMap::new();
    // 利用字符串的空格, 进行分割字符串
    for element in text.split_whitespace() {
        // 获取对应单词的位置, 如果存在, 则不会插入
        let count = map.entry(element).or_insert(0);
        // 修改当前出现的次数
        *count += 1;
    }
    // 输出内容
    println!("{:?}", map);

}

// {"worldview": 1, "world": 2, "hello": 1, "my": 1}

详细说明一下,上面的代码含义

复制代码
1、迭代器的部分:   
	代码:  
		text.split_whitespace() 
	介绍:  
		当前会根据空白字符(空格、换行等)来切割字符串,返回一个迭代器。
		在当前的例子当中, 会依次拿到数据:  "hello", "world", "my", "worldview", "world"	
	
2、核心魔法: 
	代码:
		map.entery(element)
	介绍:
		这是 Rust 的 hashMap 当中非常强大且高效的 Entry API
		它的作用: 检查 map 当中是否已经存在了 名为 element 的键
		返回值: 它会返回一个名为 Entry 的枚举 Enum 代表 这个位置可能有人(Occupied), 也可能没有人 (Vacant)

3、保底操作:
	代码:
		.or_insert(0)
	介绍:
		这是连接在 entry() 后面的方法, 其逻辑如下:
		A. 如果键存在:   直接返回指向该键对应值的 可变引用 (&mut V)
		B. 如果键不存在: 将0存入到这个键的位置, 然后返回指向这个新存入的 0 的可变引用 (&mut V)
		C. 重点: 无论键之前在不在, 执行完毕这一行之后, 变量 count 都会得到一个指向 HashMap 内部数值的 "指针" (可变引用)
		
4、更新值:  
	代码:
		*count += 1
	介绍:
		A. 由于 count 是一个可变引用(类型为 &mut i32) 我们不能直接给他加1
		B. 我们需要使用解引用符 * 星号  来找到这个引用背后的真实数据值, 然后对其进行加1的操作
		C. 因为 count 指向的是 HashMap 内部的空间, 所以这里修改会直接反映在 map 当中

逻辑流程介绍一下

复制代码
假设循环进行到了第二次, 遇见 "world" 的时候:

1、 split_whitespace   会吐出 "world"
2、 map.entry("world") 发现 "嘿, 这个单词已经在Map集合里面了, 它的值现在是1"
3、 .or_inster(0)      看到值已经存在了, 所以不会插入0, 而是直接返回了指向1的引用。
4、 *count += 1        通过引用把内存里的 1 改变成为 2

总结一下

复制代码
这种写法, 它既安全而且高效。

1、安全: 避免了先检查键是否存在, 再插入值的 "先查后改" 可能导致的逻辑错误。
2、高效: 它只进行了一次哈希计算, 如果手动调用 contains_key 判断再 insert 则需要计算两次哈希, 性能较低
相关推荐
G_dou_15 分钟前
Linux 搭建 Rust 开发环境:从 rustup 安装到 Cargo 镜像
linux·rust
swipe20 分钟前
混合检索 RAG 的工程化实践:不是多查几路,而是把召回、重排和上下文预算管好
后端·langchain·llm
uzong25 分钟前
分布式下的系统,什么是算是好的架构设计
后端·架构
hssfscv26 分钟前
QT的学习记录1
开发语言·qt·学习
SunnyDays101140 分钟前
Python操作Excel批注:从基础添加到高级自定义的完整指南
开发语言·python·excel
Yyyyyy~1 小时前
【C++】数组篇
开发语言·c++
牛肉在哪里1 小时前
ros2 从零开始27 编写广播C++
开发语言·c++·机器人
金銀銅鐵1 小时前
[Java] 如何理解 class 文件中方法的 access flags?
java·后端
夜微凉41 小时前
MySQL 事务 ACID
后端
yong99901 小时前
基于Qt的文件传输系统
开发语言·qt