一、前言
基本的数据结构各种语言都会有自己的实现,rust也不例外。
在rust中,这些数据结构的实现被称作“集合”,被放置在了std::colections
中。
内容并不算多,只有4个而已,个人认为Vec
也应该算它的一部分,可能是因为其太过于常用,所以直接将其放在了全局,而没有放在该空间内。
至于迭代器,是所有集合所通用的方法,所以也会在本章对其进行讲解。
二、Vec
首先第一个便是用的最多的集合:Vec,也就是动态数组,它可以自动扩容。
初始化它的方式主要有三种:
let v1: Vec<i32> = Vec::new();
let v2: Vec<i32> = Vec::with_capacity(1000);
let v3: Vec<i32> = vec![1, 2, 3];
第一种最常见,就是用new函数创建一个空的本实例。
第二种则是用于事先分配指定容量的数组示例,其内同样为空。
如果你事先知道这个数组里面最多存放指定个数的元素,那么使用第二种方式效率会更高,因为它就无需因空间不够导致重新分配内存、复制数据。
第三种使用vec提供的宏,通过指定元素直接生成带有数据的数组。
一般访问、修改直接用下标即可:
let mut v=vec![1,2,3];
let n=v[2];
v[1]=100;
只是需要注意一下,下标是从0开始的。
至于增加,由于是数组,一般推荐只从末尾追加,但你也可以在任意下标插入元素,只不过效率可能不够好:
fn main() {
let mut v = vec![1, 2, 3];
v.push(5); //末尾推入元素5
v.insert(0, 6); //0号位置插入元素6
}
而删除元素,也有两种方式:
let mut v = vec![1, 2, 3];
v.remove(0); //删除0位置元素1
v.drain(0..1); // 删除[0,1)这个范围中的所有元素
除了上面基本的增删改查操作外,vec还提供了一些有意思的函数,比如分页:
chunks函数可以用来做分页操作,比如上图中我设置每一页三个,然后使用skip用作”翻页“,本质上就是跳过1页,最后调用next函数获取这一页的数据。
这里的next、skip函数名之所以看起来比较奇怪,是因为它们实际上操作的是迭代器,这一点会在本章后面的内容对其进行介绍。
我们还可以判断某个元素是否存在其中:
v.contains(&4);
三、map
然后要介绍的就是我个人非常常用的集合map,也常常被称为映射。
不同于普通的数组只能通过数字索引去查找相应的元素,map可以实现将任意类型作为索引(key)去查找相应的元素(值)。
这在很多时候都是一个非常有用的特性,虽然其实现原理可能比较繁琐,但它的使用方法还是很简单的:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
//插入元素(键值对)
map.insert("a", "小明");
//取出元素(根据键取值)
let name = map["a"];
//修改元素(如果键存在则覆盖,实现修改)
map.insert("a", "小红");
//判断是否包含某个键
map.contains_key("a");
//返回个数
map.len();
//遍历
for (k,v) in map.iter() {
println!("{}:{}",k,v);
}
//删除
map.remove("a");
}
上面用的是HashMap
,前面的Hash翻译过来就是哈希的意思,是一种经典的map实现方法,除此之外还有另一种树结构的实现方式,对应于BTreeMap
,它可以保持。
两者使用方法都是一样的,只不过底层实现方式不同而已,不过就我个人来说,大多数时候我还是喜欢使用HashMap
。
四、list
链表也是我们最常使用的一个集合了,其对于需要经常增、删的情形下效率比较高,这得益于其底层的实现细节,与之相对的就是数组,在rust中与之对应的就是Vec结构。
不过这些不是本文要讨论的东西,下面直接介绍其基本的使用方法:
use std::collections::LinkedList;
fn main() {
let mut list = LinkedList::new();
//添加元素(尾部)
list.push_back("a1");
//添加元素(头部)
list.push_front("a2");