【百例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 则需要计算两次哈希, 性能较低
相关推荐
书中有颜如玉1 小时前
Kotlin Coroutines 异步编程实战:从原理到生产级应用
android·开发语言·kotlin
喜欢流萤吖~1 小时前
SpringBoot 性能优化实战
spring boot·后端·性能优化
两年半的个人练习生^_^2 小时前
每日一学:设计模式之建造者模式
java·开发语言·设计模式
常利兵2 小时前
Spring Boot 搭建邮件发送系统:开启你的邮件自动化之旅
spring boot·后端·自动化
沐知全栈开发2 小时前
Java Number & Math 类详解
开发语言
彭于晏Yan2 小时前
Spring Boot 集成邮件服务实现发送邮件功能
java·spring boot·后端
浮尘笔记2 小时前
Java Snowy 框架生产环境安全部署全流程(服务器篇)
java·运维·服务器·开发语言·后端
宸津-代码粉碎机2 小时前
Spring Boot 4.0虚拟线程实战续更预告:高阶技巧、监控排查与分布式场景落地指南
java·大数据·spring boot·分布式·后端·python
Rsun045512 小时前
6、Java 适配器模式从入门到实战
java·开发语言·适配器模式