12.集合的使用

集合

  • 集合中存储的值位于堆之上,这也是说他们不需要在编译的时候知道数据数量。
  • 栈和堆的解释在之前有过解释链接: link

vector存储一系列的值

新建vector

let v: Vec<i32> = Vec::new();
  • 上段代码创建了一个空的vector,存储类型是i32类型,
  • 而在实际过程中,由于vec!宏的存在,它会根据数据的类型为我们提供一个vec。
let v = vec![1,2,3];

更新vec

let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);
v.push(8);
  • 使用push方法在其中增加新的变量,同时注意使用mut变量进行变化。

丢弃vector会同时丢弃所有的元素

{
	let v= vec![1,2,3,4];
	
}
  • vector变量在这段代码中的作用域即为程序块中,在离开程序块之后,vector中的元素会被抛弃。如果使用vec中的元素的引用,就会出现下面的状况:

读取vector的元素

  • 读取vec中的元素有两种方法,一种是索引语法,另外一种是get方法
let v = vec![1,2,3,4,5];
let third: &i32 = &v[2];
println!("The third element is {}",third);

match v.get(2)
{
	Some(third) =>println!("The third emelnment is {}",third),
	None => println!("There is no third element."),
}
  • 使用 &[] 返回一个引用;或者使用 get 方法以索引作为参数来返回一个 Option<&T>。由于前面代码使用过third来获取&v[2]中的内容,所以之后的结果中显而易见,使用option<&T>来获取third的引用。
  • 关于Option<&T>的有关内容,链接: link
使用索引语法或者get获取vector中的项。
let v = vec![1,2,3,4,5];
let does_not_exist = &v[100];
let does_not_exist = v.get(100);

官方解释:

  • 当运行这段代码,你会发现对于第一个 [] 方法,当引用一个不存在的元素时 Rust 会造成 panic。这个方法更适合当程序认为尝试访问超过 vector 结尾的元素是一个严重错误的情况,这时应该使程序崩溃。

  • 当 get 方法被传递了一个数组外的索引时,它不会 panic 而是返回 None。当偶尔出现超过 vector 范围的访问属于正常情况的时候可以考虑使用它。接着你的代码可以有处理 Some(&element) 或 None 的逻辑,如第 6 章讨论的那样。例如,索引可能来源于用户输入的数字。如果它们不慎输入了一个过大的数字那么程序就会得到 None 值,你可以告诉用户当前 vector 元素的数量并再请求它们输入一个有效的值。这就比因为输入错误而使程序崩溃要友好的多!

vector中相同作用域中不可以同时存在可变和不可变引用
  • 使用官方文案进行解释
    在这里插入图片描述
    vector中的内存位于堆之上,而堆中的内存是提前分配好大小的,这样的结果会造成可能添加新元素的结果中会添加新的内存空间,而之前的内存空间的地址引用会因此而改变,这样的结果是不可估计的,Rust会自动阻止这种情况的发生。

vector中元素的遍历

let v =vec![100,32,57];
for i in &v
{
	println!("{}",i);
}
  • 这种是直接遍历其中的元素,下面则是对可变vector中的元素每个均添加50
let mut v = vec![100,32,57];
for i in &mut v
{
	*i +=50;
}

使用枚举存储多种类型

  • vector只能存储同一种类型的元素,而之前介绍过的有元组、枚举均可添加不同类型的值。
enum SpreadsheetCell{
	Int(i32),
	Float(f64),
	Text(String),
}
let row = vec![
	SpreadsheetCell::Int(3),
	SpreadsheetCell::Float(10.12),
	SpreadsheetCell::Text(String::from("blue")),
];

字符串(String)的使用

新建字符串

let mut s = String::new();
  • 新建空的字符穿串
  • to_string方法,能够用于任何实现了Display trait类型,字符串字面量也实现了它,
let data = "initial contents";
let s= data.to_string();
let s= "initial contents".to_string();
  • 这两种方法均实现了string字符串,第一种方法是使用了先创建字符串字面量,第二种方法是直接使用字符串字面量进行。
  • 同时也可以通过使用String::from从字符串字面量创建String,等同于to_string.
let s = String::from("initial contents");

同时,rust使用的unicode中包括了多种数据,类似特殊字符和emoji也可以。

fn main() {
let hello = String::from("السلام عليكم");
let hello = String::from("Dobrý den");
let hello = String::from("Hello");
let hello = String::from("שָׁלוֹם");
let hello = String::from("नमस्ते");
let hello = String::from("こんにちは");
let hello = String::from("안녕하세요");
let hello = String::from("你好");
let hello = String::from("Olá");
let hello = String::from("Здравствуйте");
let hello = String::from("Hola");
let hello = String::from("😻");
}

更新字符串

  • String中的内容可以改变,大小也可以改变,类似于在vec之中放入更多的数据,同时可以方便使用+或者format!来拼接String值。
使用push_str和push附加字符串
  • 通过使用push_str方法添加字符串slice,可以增加string的长度。
let mut s = String::form("foo");
s.push_str("bar");
  • 结果是foobar
  • 使用push_str获取的时候,使用字符串slice的话,会夺走它的所有权。
fn main() {
let mut s1 = String::from("foo");
let s2 = "bar";
s1.push_str(s2);
println!("s2 is {}", s2);
}
  • 这段代码可以正常运行,虽然s2的所有权被函数获取了。
    在这里插入图片描述
  • 使用push方法将字符加入到String之中,它会获取一个单独的字符为参数,附加到String之中。
fn main() {
let mut s = String::from("lo");
s.push('l');
}
使用+运算符或者format!宏拼接字符串
let s1 = String::from("hello,");
let s2 = String::from("world!");
let s3 = s1+&s2;
  • 在使用完成之后,s3将包括s1s2之中的内容,但是s1是使用的函数本身,而&s2是使用s2的引用,因此,s2之后仍具有所有权,而s1不行,这种情况是由add函数所定义的。
  • 具体可以看下面的官方文档
    在这里插入图片描述
  • 而对于多个字符串的连接,则需要使用format!宏定义,如果使用add函数会显得较为复杂。
//使用add函数
fn main() {
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");

let s = s1 + "-" + &s2 + "-" + &s3;
}
//使用format!宏
fn main() {
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");

let s = format!("{}-{}-{}", s1, s2, s3);
}
字符串的索引

Rust中的字符串不支持索引,因为

  • 每一个unicode在不同语言之下,它的字节数不同
  • 字符串中包括字节、标量值和字形簇
  • 字符串中的时间复杂度未知,因为要从开始直到结束处遍历字符总数
    下面是官方描述
    在这里插入图片描述
遍历字符串的方法
  • Rust对字符串元素的获取方法有另外的方法。
    • 使用chars方法来对字符串进行遍历。
      for c in "aaaasssss".chars(){ println!("{}",c);}
  • bytes方法返回每个原始字节。
    • 使用bytes方法返回原始字节
for b in "aaaacccc".bytes()
{
	println!("{}",b)
}
  • 这段代码会打印出对应的String字节,对应的值为unicode格式。此处应该熟记a=97,A=65,大小写转换差32。

哈希(hash) map

  • HashMap<K,V>类型储存了一个键类型k对应一个值类型V的映射,通过哈希函数实现映射,决定如何将键和值放入内存中。
  • 哈希map使用键作为寻找数据的方法,与vector不同。
  • 哈希表没有被prelude引用,需要自己添加头文件
  • 哈希表将数据存储在堆之上。
新建哈希表
use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"),10);
scores.insert(String::from("Yellow"),50);
  • 使用new创建新的哈希表,并且使用insert插入数据。
  • 创建之后Blue有10分,Yellow有50分
  • 另外一种方法是使用collect方法创建
  • collect方法是使用zip方法创建元组vector,然后再使用collect方法将vector转换为HashMap。
fn main() {
use std::collections::HashMap;

let teams  = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];

let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
}

  • HashMap<_ , _>类型标注和其他的不同,在其他的数据中使用collect显示转换,而HashMap则不同,只需使用下划线_进行标注,同时根据后面的vector来推断前面的类型。
哈希map和所有权

i32是一种Copy trait类型,关于Copy trait的解释,链接: link

  • 对于像String之类的数据,他的所有权会被移动给HashMap,而Copy trait不会
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_namefield_value就不能够再使用。不想要这种结果,可以使用引用来传递值,但是这些值的生命周期必须至少包括HashMap的生命周期
HashMap中值的访问
  • 使用get方法并提供对应键从哈希map中获取值
fn main() {
use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

let team_name = String::from("Blue");
let score = scores.get(&team_name);
}
  • get返回的Option<V>类型,链接: link
  • 所以此时返回的值为Option<i32>,对应着的是some(10),而对应着的,如果返回值为None,则需要使用match匹配。
  • 如果想要遍历哈希map中的每一个键值对,使用for循环:
fn main() {
use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

for (key, value) in &scores {
    println!("{}: {}", key, value);
}
}

更新哈希map

  • 哈希Hash中可以选择完全无视旧值并用新值代替旧值。可以选择保留旧值而忽略新值,并只在键 没有 对应值时增加新值。或者可以结合新旧两值。
覆盖一个值
  • 哈希表之中不能够使用#[derive(Debug)],这种形式仅仅在enum,struct、union中使用,若是想打印,直接打印即可
fn main() {
use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);

println!("{:?}", scores);
}
只在键没有对应值时插入
fn main() {
use std::collections::HashMap;

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

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

println!("{:?}", scores);
}
  • entry的返回值是一个枚举,如果有的话,就不执行or_insert,如果没有,执行or_insert
根据旧值更新一个值
use std::collection::HashMap;

let text = "hello world wonderful world";

let mut map = HashMap::new();

for word in text.split_whiltespace()
{
	let count = map.entry(word).or_insert(0);
	*count += 1;
}
println!("{:?}",map);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值