RUST 学习日记 第11课 ——向量

RUST 学习日记 第11课 ——向量(动态数组)


0x00 回顾与开篇

上一节主要讲解了Rust的复合数据类型(Compound Types)——数组,元组。这节课继续讲解数据类型——向量(Vector),又称作动态数组。为跟数组区分且方便理解,课程中将把动态数组全部叫作“向量”。上节课只是简单介绍了下数组的概念。这节讲的向量跟上节讲的数组有点儿相似,我对对比数组一起来学习向量。它们到底有什么不同呢?下面告诉你答案。

0x01 向量的定义

Vec是一种动态的可变数组,可以在运行时增长或者缩短数组的长度。其签名形式Vec<T>,T表示任意类型。Vec<T>叫做T类型的向量。向量的元素保存在上,这也是它可以动态增长或者缩短长度的原因。向量中的每个元素都分配有唯一的索引值,同数组一样,索引从0开始计数。

PS:本节课中的代码打印时都添加了 “&” 符号,其与Rust的所有权有关,这里了解即可,后面会详细介绍。

0x02 向量的创建

向量常见的创建方式有以下3种:

1、使用Vec::new()创建一个空的向量。容量为0,长度为0的向量在堆上是不占内存的。通常创建向量时使用mut修饰,因为向量需要动态增删元素。需要指定类型。

let mut vec_empty: Vec<i32> = Vec::new();

2、[推荐方法] 初始化时为向量指定容量。容量不等于长度,不要跟长度混淆。下面是容量为10的空向量(长度为0)。需要指定类型。

let mut vec_capacity :Vec<i32> = Vec::with_capacity(5);

3、[推荐方法] 使用“宏”创建向量。这种方式的创建方法类似于数组的语法。它也有3种创建方式。

  • 创建空的向量。需要指定类型。
 let mut vec_marco: Vec<i32> = vec![];
  • 创建带有默认元素的向量。支持类型推断。
let mut vec_marco = vec![1, 2, 3, 4, 5];
  • 创建指定长度且初始化所有元素的向量。支持类型推断。
// 长度为5,元素初始化为0
let mut vec_marco = vec![0; 5];

0x03 添加元素

使用push方法在向量的尾部添加新元素。下面的代码是在向量后面追加了元素1。添加元素需要将向量使用mut关键字修饰。

let mut vec_push = vec![0; 5];
vec_push.push(1);
dbg!(&vec_push);

0x04 修改元素

修改元素,直接使用“变量名称[索引] = 要修改的值”即可重新为元素赋值。下面的代码是将向量的第4个元素(索引值3的元素)的值修改为1。修改元素需要将向量使用mut关键字修饰。

let mut vec_push = vec![0; 5];
vec_push[3] = 1;
dbg!(&vec_push);

0x05 删除元素

Rust中有两种删除向量的元素的方式。删除元素需要将向量使用mut关键字修饰。

1、通过pop方法弹出队尾元素。调用pop方法会返回一个Option枚举类型。如果数组不为空,则会返回Some(v)v是弹出的值。如果向量为空,则会返回None(关于Option这里了解即可)。

示例代码如下:

    let mut vec_pop = vec![1];
    let pop = vec_pop.pop();
    dbg!(pop);
    let pop = vec_pop.pop();
    dbg!(pop);

代码执行结果:

[src\main.rs:31] pop = Some(
    1,
)
[src\main.rs:33] pop = None

2、通过remove方法删除元素,需要传入将要删除元素的索引,并且返回删除的元素。这个操作会引发向量元素的移位,被删除元素的后面元素都会相应的左移一位。如果传入的索引大于向量的长度,则会产生程序错误

示例代码如下:

let mut vec_remove = vec!['w', 'o', 'r', 'l', 'd'];
    let remove_element = vec_remove.remove(3);
    dbg!(remove_element);
    // 索引越界,会发生错误
    // let remove_element = vec_remove.remove(5);

代码执行结果:

[src\main.rs:38] remove_element = 'l'

// 下面是越界的执行结果
thread 'main' panicked at 'removal index (is 5) should be < len (is 4)', library\alloc\src\vec\mod.rs:1347:13
stack backtrace:

0x06 访问元素

Rust中也存在两种方式去访问向量中的元素。

1、使用“向量名称[索引]”的方式访问指定元素,类似于修改元素的访问。如果传入的索引大于向量的长度,则会产生程序错误,常称作数组越界

示例代码如下:

    let vec_find = vec![1, 2, 3];
    dbg!(vec_find[0]);
    dbg!(vec_find[1]);
    dbg!(vec_find[2]);  

代码执行结果:

[src\main.rs:44] vec_find[0] = 1
[src\main.rs:45] vec_find[1] = 2
[src\main.rs:46] vec_find[2] = 3

2、使用get方法访问元素,传入的参数同样是索引,但是其犯规之是Option枚举类型,如果索引越界,不会产生错误,则会返回None。(关于Option这里了解即可)。

示例代码如下:

    let vec_get = vec![1, 2, 3];
    dbg!(vec_get.get(0));
    dbg!(vec_get.get(1));
    dbg!(vec_get.get(2));
    // 下面代码不会产生错误,正常执行
    dbg!(vec_get.get(3));

代码执行结果:

[src\main.rs:52] vec_get.get(0) = Some(
    1,
)
[src\main.rs:53] vec_get.get(1) = Some(
    2,
)
[src\main.rs:54] vec_get.get(2) = Some(
    3,
)
[src\main.rs:56] vec_get.get(3) = None

0x07 向量容量的变化

向量容量增长策略

向量的容量(Capacity)是指为存储元素所分配的空间。向量的长度(Length)如果大于其当前的容量,则会发生重新分配空间的操作,这个过程比较耗时。假设当前数组的容量为5,如果向量内的元素小于等于5个,则其容量不会增加,如果元素超过5个,则向量的容量就会重新分配,在原有的基础上乘以2(目前2是向量的增长因子),最后容量会变成10。因此尽可能的在初始化时为其指定合适的容量。

可以通过len()方法获取向量的长度,capacity()方法获取向量的容量,容量变化的示例代码如下:

let mut vec_capacity :Vec<i32> = Vec::with_capacity(5);
    // 动态增长
    println!("vec_capacity 填充元素前的长度为 {}", vec_capacity.len());
    println!("vec_capacity 填充元素前的容量为 {}", vec_capacity.capacity());
    // 关于循环这里了解即可,这里的意思是,填充0,1,2,3,4五个元素
    for i in 0..5 {
        vec_capacity.push(i);
    }
    println!("vec_capacity 填充5个元素后的长度为 {}", vec_capacity.len());
    println!("vec_capacity 填充5个元素后的容量为 {}", vec_capacity.capacity());

    // 填充第6个元素
    vec_capacity.push(5);

    println!("vec_capacity 填充6个元素后的长度为 {}", vec_capacity.len());
    println!("vec_capacity 填充6个元素后的容量为 {}", vec_capacity.capacity());

代码执行结果:

vec_capacity 填充元素前的长度为 0
vec_capacity 填充元素前的容量为 5
vec_capacity 填充5个元素后的长度为 5
vec_capacity 填充5个元素后的容量为 5
vec_capacity 填充6个元素后的长度为 6
vec_capacity 填充6个元素后的容量为 10
首次填充元素的默认容量

咱们再来看另外一个问题,假设创建一个容量为0的向量,然后向其中添加元素,这时容量会变成多少呢?

示例代码如下:

    let mut vec_default: Vec<i32> = Vec::new();
    println!("vec_default 的长度为 {}", vec_default.len());
    println!("vec_default 的容量为 {}", vec_default.capacity());
    vec_default.push(1);
    println!("vec_default 添加一个元素的长度为 {}", vec_default.len());
    println!("vec_default 添加一个元素的容量为 {}", vec_default.capacity());

代码执行结果:

vec_default 的长度为 0
vec_default 的容量为 0
vec_default 添加一个元素的长度为 1
vec_default 添加一个元素的容量为 4

从上面程序的运行结果,可以得知,首次创建空容量的向量,向其中添加一个元素后,则会将容量变调整为4。之后的容量调整则遵循增长因子为2的规则。

0x08 数组与向量的区别

  • 原生数组(Array)的签名是[T;N],而向量(Vector)的签名是Vec<T>

  • 原生数组(Array)的大小是在编译时确定的常量,也是类型自身的一部分。其大小不更改,不能像数组中增加或者缩短数组的长度。向量(Vector)是一种可以动态分配增长或者缩减的数组,在运行时才会确定向量的长度。

  • 原生数组(Array)的元素可以在上存储,而向量(Vector)的元素只能在上分配。

0x09 小结

其实Vec<T>包好了3个值:分别是:对分配在对上用于保存元素的缓冲区的引用,该缓冲区可以存储元素的个数(Capacity),当前实际存储的元素个数(Length)。如果提前知道向量的容量,推荐使用Vec::with_capacity。现在知道了向量是由三部分组成,那么它在内存中的模型是什么样的呢?关于向量和数组该如何遍历?排序?其它高级用法?这一系列的问题将会在后续章节一一介绍,这节课仅仅是简单介绍了向量这种数据类型。

0x10 本节源码

011 · StudyRust - 码云 - 开源中国 (gitee.com)

下节预告——了解Rust的另一种数据类型——切片(Slice)。还有切片与数组、向量的区别。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355