集合-Rust

概述

储存在堆上,不必在编译时就已知,可以随着程序的运行增长或缩小

三种常见集合

vector 允许我们一个挨着一个地储存一系列数量可变的值
字符串(string)是字符的集合。我们之前见过 String 类型,不过在本章我们将深入了解。
哈希 map(hash map)允许我们将值与一个特定的键(key)相关联。这是一个叫做 map
的更通用的数据结构的特定实现。

Vector

简单使用

标准库提供,可以存储多个值,只能存储类型相同的值,在内存中连续存放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn main() {
//let v1:Vec<i32> = Vec::new();//创建为初始化的vec需要指定变量的类型
let mut v= vec![0,1,2];//使用宏创建vec由于初始化的值,编译器会自动推断

v.push(3);//使用push添加元素
let th = &v[2];//使用索引获取值
println!("vec 3 number is {th}");

match v.get(5) {//使用get获取值
Some(v)=>println!("vec 3 number is {v}"),
None=>println!("vec 3 number is null"),

}

}

Vector在拥有借用的时候不可以添加新数值,因为添加数值有可能导致老的数据丢失

遍历

1
2
3
4
5
6
7
8

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

for i in v{
println!("{i}");
}
}

配合枚举

借助枚举在vec存储不同类型的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#[derive(Debug)]
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}

fn main() {
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];

for i in row{
println!("{:?}",i);
}

}

String

简单使用

字符串在Rust中是基于byte类型的集合,核心层面字符串类型是指字符串切片str,String类型来自标准库

创建新String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

fn main() {

let mut s=String::new();//一般不采用

let data = "initial contents";

s=data.to_string();

let s1="initial contents".to_string();//可以直接使用字符子面值调用to_string,只用于实现了display trait的类型

let s2 = String::from("initial contents");


}

更新String

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

fn main() {

let mut s = String::from("foo");
s.push_str("bar");//push_str,字符串切片

s.push('l');//push,单个字符

let h = String::from("hello");
let w = String::from("world!");
let hw=h + &w;//+拼接,第二个参数需要字符串切片类型,类似fn add(self,&str)->String,字符串String的引用被强转为了字符串切片
println!("{}",hw);

let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{s1}-{s2}-{s3}");//format!宏,不会取得参数所有权

}

切割

字符串不允许索引方式访问,但是可以通过索引切分字符串

image-20241216190525030

需要注意的是切割字符串必须沿着边界切割,比如中文一个字两个字节,如果我们切分三个字节就会pannic

image-20241216191131111

HashMap

HashMap<K, V>,型储存了一个键类型 K对应一个值类型 V 的映射

哈希 map 是同质的:所有的键必须是相同类型,值也必须都是相同类型。

新建

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

fn main() {

let mut scores = HashMap::new();
scores.insert(String::from("blue"), 10);
scores.insert(String::from("yellow"), 50);

访问

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("blue"), 10);
scores.insert(String::from("yellow"), 50);

let k = String::from("blue");
let v=scores.get(&k);//get返回option类型,所以需要match匹配
match v {
Some(i)=>println!("{i}"),
None=>println!("null"),
}

}

所有权

对于像 i32 这样的实现了 Copy trait 的类型,其值可以拷贝进哈希 map。对于像 String 这样拥有所有权的值,其值将被移动而哈希 map 会成为这些值的所有者

1
2
3
4
5
6
7
8
9
fn main() {
use std::collections::HashMap;
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(field_name, field_value);
// 这里 field_name 和 field_value 不再有效,
// 尝试使用它们看看会出现什么编译错误!
}

遍历

image-20241216192918630

修改

插入新的键值对,已经存在的key会覆盖原有的值

image-20241216193258358

插入新的键值对,不存在key的时候添加一个新的键值对,存在key那么不要修改已经存在的值

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("Blue"), 10);

scores.entry(String::from("Yellow")).or_insert(50);//entry检查k是否有对应v

scores.entry(String::from("Blue")).or_insert(50);

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

找到一个键对应的值并根据旧的值更新它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//split_whitespace 方法返回一个由空格分隔 text 值子 slice 的迭代器。or_insert 方法返回这个键的值的一个可变引用(&mut V)。这里我们将这个可变引用储存在 count 变量中,所以为了赋值必须首先使用星号(*)解引用 count
use std::collections::HashMap;
fn main() {

let text = "hello world wonderful world";

let mut map = HashMap::new();

for word in text.split_whitespace() {

let count = map.entry(word).or_insert(0);
*count += 1;

}
println!("{map:?}");
}