七、常用集合
1. Vector
-
作用
- 在一个单独的数据结构中储存多于一个的值,它在内存中彼此相邻地排列所有的值
-
注意
vector只能储存相同类型的值- 在
vector的结尾增加新元素时,如果没有足够空间将所有所有元素依次相邻存放,就可能会要求分配新内存,并将之前的元素拷贝到新的空间中,所以效率可能相对会比较低
-
vector存储不同类型的值- 当需要在
vector中储存不同类型值时,我们可以使用一个定义好的枚举来包裹这些值 - 限制:必须提前知道要存入这个
vector的所有类型
- 当需要在
-
使用示例
fn main() { // 创建方式 1: 使用 Vector::new let mut v1: Vec<i32> = Vec::new(); // 创建方式 2:使用 vec! 宏 let mut v2 = vec![1, 2, 3]; // 向 vector 内增加元素 v1.push(4); // 向末尾追加一个元素 // 获取方式 1 - 索引 let first: &i32 = &v1[0]; // 返回的是引用 // 获取方式 2 - get 方法 let second: Option<&i32> = v1.get(1); // 返回的是枚举 // 遍历 vector for item in &v2 { println!("{}", item); } // 来自于官方教程中的一个示例 - 遍历并修改 vector 每个元素的值 let mut v = vec![100, 32, 57]; for i in &mut v { *i += 50; // 注意这个解引运算符 `*` } }
2. 字符串
-
字符串是作为字节的集合外加一些方法实现的
-
字节的字符编码格式:UTF-8
-
在 Rust 中,核心只有一种字符串类型:
str,即字符串 slice,它通常以被借用的形式 (&str) 出现
2.1 创建
fn main() {
// 方式 1:使用 String::new
let mut s1 = String::new();
// 方式 2:从字面量转换来
let mut s2 = "abc".to_string();
// 方式 3:使用 String::from 转换字面量
let mut s3 = String::from("abc");
// 官方教程提示:大部分情况下,String::from 和 .to_string 最终做了完全相同的工作,所以如何选择就是风格问题了
}
2.2 更新
fn main() {
let mut s = "abc".to_string();
// 追加一个字符串 slice:push_str
s.push_str(" abc");
// 追加单个字符:push
s.push("d");
// 使用 + 运算符拼接两个字符串
let s1 = "aaa".to_string();
let s2 = "bbb".to_string();
let s3 = s1 + &s2; // 注意:这里除了第一个变量外,后续的字符串变量都必须取引用
println!("s2 = {}, s3 = {}", s2, s3); // 不能访问 s1,因为 s1 已经被移动了
// 使用 format!宏 拼接字符串,与使用 println 的语法一模一样
let print_content = format!("s2 = {}, s3 = {}", s2, s3);
}
2.3 访问字符
-
注意:Rust 中,字符串 不支持 按索引访问
String是一个Vec<u8>的封装- 索引操作预期总是需要常数时间 (O(1))。但是对于
String不可能保证这样的性能,因为 Rust 必须从开头到索引位置遍历来确定有多少有效的字符
-
有效的 Unicode 标量值可能会由不止一个字节组成
-
一个英文字符:1 字节
-
一个梵文字符:2 字节
-
一个中文字符:3 字节
-
一个 emoji 字符:4 字节
fn main() { let a = String::from("a"); println!("`a` 的长度:{}", a.len()); let e = String::from("З"); println!("`З` 的长度:{}", e.len()); let heart = String::from("❤"); println!("`❤` 的长度: {}", heart.len()); let hello = String::from("哈"); println!("`哈` 的长度: {}", hello.len()); let hot = "🥵"; println!("`🥵` 的长度:{}", hot.len()); }
-
-
按 “字符” 访问
fn main() { // 你可以尝试一下把下面这些字符串按字符打印出来看一下 print_char("السلام عليكم"); print_char("Dobrý den"); print_char("Hello"); print_char("שָׁלוֹם"); print_char("नमस्ते"); print_char("こんにちは"); print_char("안녕하세요"); print_char("你好"); print_char("Olá"); print_char("Здравствуйте"); print_char("Hola"); } fn print_char(str: &str) { for c in str.chars() { print!("{}, ", c) } print!("\n"); }
3. Hash Map
- Hash Map 用于储存一个键类型
K对应一个值类型V的映射- 类型:
HashMap<K, V> HashMap通过一个hash函数来实现这个映射关系
- 类型:
hash函数- 默认使用的是 “密码学安全的 (cryptographically strong)“ 的哈希函数
- 优点:可以抵抗拒绝服务(Denial of Service, DoS)攻击
- 缺点:相对性能不够高
- Rust 支持手动指定一个自定义的
hash函数
- 默认使用的是 “密码学安全的 (cryptographically strong)“ 的哈希函数
3.1 创建
use std::collections::HashMap;
fn main() {
// 方式 1:使用 HashMap::new
let mut map1: HashMap<String, u8> = HashMap::new();
// 方式 2:使用元组 vector 的 collect 方法
// 来自官方教程的一个示例
let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
// 使用下划线占位即可,Rust 会推断对应的类型
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
}
3.2 使用
- 常用 API
get(&key):根据 key 来获取值,可能为Noneinsert(key, value):不管 hash 表里有没有,都直接插入,会覆盖已有值entry(key).or_insert(value):如果 hash 表里没有,就插入;如果 hash 表里有,就忽略
use std::collections::HashMap;
fn main() {
hash_map_test();
}
fn hash_map_test() {
// 创建一个 hash map
let mut scores: HashMap<String, i32> = HashMap::new();
let jelly = String::from("Jelly");
let kate = String::from("Kate");
// 将数据加入到 hash map 中
scores.insert(jelly, 50);
scores.insert(kate, 25);
// 已经有 Jelly 了,insert 会直接覆盖值
let jelly = String::from("Jelly");
scores.insert(jelly, 75);
// entry 只在没有对应的 key 时才插入值
scores.entry(String::from("Jelly")).or_insert(50); // 不会插入
scores.entry(String::from("Yellow")).or_insert(50); // 会插入
// ! 注意:这个遍历是无序的
println!("{:?}", scores); // 快捷打印 hash map 的一种方式
item_get_test(&scores);
println!("{:?}", scores);
let changed_scores = update_item_value(&scores);
println!("{:?}", changed_scores);
}
fn item_get_test(scores: &HashMap<String, i32>) {
let jelly = String::from("Jelly");
let jelly_score = scores.get(&jelly);
// 获取值。在生产中,一定要注意判空
if !jelly_score.is_none() {
println!("Jelly's score is {}", jelly_score.unwrap());
}
let cherry = String::from("Cherry");
let cherry_score = scores.get(&cherry);
if !cherry_score.is_none() {
println!("Cherry's score is {}", cherry_score.unwrap());
} else {
println!("Cherry doesn't has any score...");
}
}
fn update_item_value(scores: &HashMap<String, i32>) -> HashMap<String, i32> {
let mut result: HashMap<String, i32> = HashMap::new();
// 使用 for..in 遍历 hash map
for (key, value) in scores {
let value = result.entry(key.clone()).or_insert(value.clone());
*value += 1; // 注意这个解引运算符
}
return result;
}