1简介
rust标准库std::collections也提供了像C++ STL库中的容器,分为4种通用的容器,8种类型,如下表所示。
线性容器类型:
名称 | 简介 |
---|---|
Vec<T> | 内存空间连续,可变长度的数组,类似于C++中Vector<T>容器 |
VecDeque<T> | 内存空间连续,可变长度的双端队列,类似于C++中Deque<T>容器 |
LinkedList<T> | 内存空间不连续,双向链表,类似于C++中的list<T> |
key-value 键-值对存储
名称 | 简介 |
---|---|
HashMap<K,V> | 基于哈希表的无序键-值对,类似于C++ STL中unordered_map<K,V> |
BTreeMap<K,V> | 基于B树的有序键-值对,类似于C++ STL中map<K,V>,不过map是基于红黑色的实现的 |
集合
名称 | 简介 |
---|---|
HashSet<T> | 基于哈希表实现的无序集合,类似于C++ STL中的set<T> |
BTreeMap<T> | 基于B数的有序集合,类似于C++ STL中的unordered_set<T> |
优先队列
名称 | 简介 |
---|---|
BinaryHeap<T> | 基于二叉堆的优先队列,类似于C++ STL中的priority_queue<T> |
2. Vec使用方式
Vec是一种动态可变长数组(简称动态数组),即在运行时可增长或者缩短数组的长度。动态数组在内存中开辟了一段连续内存块用于存储元素,且只能存储相同类型的元素。新加入的元素每次都会被添加到动态数组的尾部。动态数组根据添加顺序将数据存储为元素序列。序列中每个元素都分配有唯一的索引,元素的索引从0开始计数。
下面例子给出常用的10种使用vec的方式,使用示例如下:
fn main() {
//1.使用Vec::new()方式创建
let mut v1:Vec<i32> = Vec::new();
// 2.使用vec!宏创建,创建动态数组的同时进行初始化
let mut v2:Vec<i32> = vec![1,2,3,4,5];
// 3.使用Vec::with_capacity函数创建指定容量的动态数组
let mut v3:Vec<i32> = Vec::with_capacity(1024);
//4.使用push方法向动态数组中添加元素
v1.push(10);
v2.push(11);
v3.push(12);
//5. 使用[]访问动态数组中的元素
println!("{} {} {}", v1[0], v2[0], v3[0]);
//5.1修改动态数组中的元素
v1[0] = 100;
v2[0] = 200;
v3[0] = 300;
//6.使用get方法访问动态数组中的元素
//这种方式可以避免下标索引越界,get()返回的是Option枚举,所以使用match进行匹配解构
match v1.get(0) {
Some(v) => println!("v1[0]={}", v),
None => println!("v1[0] is None"),
};
//7.使用pop()删除最后一个元素,并返回最后一个元素
match v2.pop() {
Some(val)=>println!("v2.pop()={}", val),
None=>println!("v2 is empty"),
}
//8.使用remove()删除指定索引的元素,并返回被删除的元素
let ret = v3.remove(0);
println!("remove elem is:{}", ret);
//9.使用iter_mut() 迭代器方法遍历并修改动态数组中的元素
for i in v1.iter_mut() {
*i += 100;
println!("v1={}", i);
}
//10.使用iter() 迭代器方法遍历动态数组中的元素
for i in v2.iter() { //这个方法是不能修改元素的
println!("v2={}", i);
}
println!("end: v1={:?} v2={:?} v3={:?}", v1, v2, v3);
}
输出内容:
3.VecDeque使用方式
双端队列是一种同时具有栈(先进后出)和队列(先进先出)特征的数据结构,适用于只能在队列两端进行添加或删除元素操作的应用场景。使用VecDeque结构体之前需要显式导入std::collections::VecDeque。
在步骤7中使用remove删除元素,我们这里使用了一种新的语法,if let
进行匹配函数返回的枚举类型,它的作用和match类似,但是它只匹配其中某个枚举类型,在某些我们只关注某个枚举类型时候简化语法。使用示例如下:
use std::collections::VecDeque;
fn main() {
//1. 使用VecDeque::new创建方式
let mut vd1:VecDeque<u32> = VecDeque::new();
//2.使用数组进行创建并初始化,10个元素,值为0
let mut vd2:VecDeque<u32> = VecDeque::from([0;10]);
//3.使用vec!进行初始化
let mut vd3:VecDeque<u32> = VecDeque::from(vec![1,2,3,4,5]);
//4.使用VecDeque::with_capacity创建指定容量的VecDeque
let mut vd4:VecDeque<u32> = VecDeque::with_capacity(1024);
//5.添加元素
vd1.push_back(101); //从尾部添加元素
vd1.push_front(102); //从头部添加元素
//6.索引 修改元素
vd2[0] = 100;
match vd2.get(0){ //get(index)返回Option枚举,需要match解构
Some(val) => println!("vd2[0]={}", val),
None => println!("vd2[0] is None"),
}
//7.删除元素
match vd3.pop_back(){ //从尾部删除元素 返回Option需要match解构
Some(val) => println!("pop_back() elem={}", val),
None => println!("vd3 is empty!!!"),
}
match vd3.pop_front() { //从头部删除元素 返回Option需要match解构
Some(val) => println!("pop_front() elem={}", val),
None => println!("vd3 is empty!!!"),
}
if let Some(val) = vd3.remove(2){ //remove(index)删除指定索引的元素
println!("remove index 2 elem={}", val);
//这里使用的if let语法,remove结果返回Option枚举,
//如果枚举是Some(val),就进入if let块,如果枚举是None,就进入else块或不处理
//if let语法和match语法的区别,match语法是匹配所有枚举类型,if let语法是匹配某一种枚举类型
//if let语法可以用在返回值是枚举的函数中,简化代码编写
}
println!("vd1={:?} vd2={:?} vd3={:?} vd4={:?}", vd1, vd2, vd3, vd4);
}
输出内容:
4. LinkedList使用方式
链表(LinkedList) 是一种动态数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针(单链表)或指向前一个和下一个节点的指针(双链表)。
使用示例:
use std::collections::LinkedList;
fn main() {
//1.使用LinkedList::new创建方式
let mut l1:LinkedList<u32> = LinkedList::new();
//2.使用数组进行初始化
let mut l2:LinkedList<u32> = LinkedList::from([1,2,3,4,5]);
//3. 添加元素
l1.push_front(10); //从头部添加元素
l1.push_back(10); //从尾部添加元素
//4.删除元素
match l2.pop_front(){ //从头部删除元素
Some(val) => println!("pop_front() elem={}", val),
None => println!("l2 is empty!!!"),
}
//5.遍历 修改元素
for i in l2.iter_mut() {
*i += 100;
println!("l2={}", i);
}
println!("l1={:?} l2={:?}", l1, l2);
}
输出信息:
5.HashMap使用方式
哈希表(HashMap)是基于哈希算法来存储键-值对的集合,其中所有的键必须是同一类型,所有的值也必须是同一类型,不允许有重复的键,但允许不同的键有相同的值。使用HashMap结构体之前需要显式导入std::collections::HashMap。
使用示例:
fn main() {
//1.使用HashMap::new函数创建空的HashMap
let mut hm1:HashMap<&str, i32> = HashMap::new();
//2.使用HashMap::from函数创建HashMap 使用数组进行初始化
let mut hm2:HashMap<&str, i32> = HashMap::from([("a", 1), ("b", 2), ("c", 3)]);
//3.使用HashMap::with_capacity函数创建指定容量的HashMap
let mut hm3:HashMap<&str, i32> = HashMap::with_capacity(1024);
//4.添加元素、修改元素
//使用insert方法在HashMap中插入或更新键-值对。
//如果键不存在,执行插入操作并返回None。
//如果键已存在,执行更新操作,将对应键的值更新并返回旧值。
if let None = hm1.insert("h1", 1){ //如果返回None,说明插入成功
println!("new insert val 1");
}
if let Some(old) = hm1.insert("h1", 11){ //如果返回旧值,说明更新成功
println!("insert old val:{}", old);
}
//5.使用entry和or_insert方法检查键是否有对应值,
//没有对应值就插入键-值对,已有对应值则不执行任何操作。
hm3.entry("zhangsan").or_insert(32);
hm3.entry("lisi").or_insert(42);
hm3.entry("wangwu").or_insert(42);
//6.使用iter()方法遍历HashMap
for (key, value) in hm2.iter_mut() {
println!("key={}, value={}", key, value);
*value += 100;
}
//7.使用remove方法删除并返回指定的键-值对,如果不存在就返回None
if let Some(v) = hm3.remove("wangwu"){
println!("remove key=wangwu, value={}", v);
}
//8.使用get方法获取指定键对应的值,如果不存在就返回None
if let Some(v) = hm3.get("lisi"){
println!("get key=lisi, value={}", v);
}
//9.使用contains_key方法检查HashMap中是否存在指定的键,返回布尔值
println!("hm3 contains key=lisi:{}", hm3.contains_key("lisi"));
//10.使用【】进行索引xuan
println!("hm3={}", hm3["zhangsan"]);
println!("hm1:{:?} hm2:{:?} hm3:{:?}", hm1, hm2, hm3);
}
输出结果:
6.BTreeMap使用方法
-
使用BTreeMap存储的KV键值对,Key存储是有序的,除了像HashMap一样插入、删除、修改外,还支持range遍历,即范围遍历查找。
-
这是因为BTreeMap基于B树实现的,底层数据结构是一个自平衡的b树,确保有效的插入、删除和查找,键按排序顺序存储和访问,支持高效的范围查询和有序迭代。
-
使用示例:
use std::collections::BTreeMap;
fn main() {
//1.使用BTreeMap::new函数创建空的BTreeMap
let mut bm1:BTreeMap<&str, i32> = BTreeMap::new();
//2.使用BTreeMap::from函数创建BTreeMap 使用数组进行初始化
let mut bm2:BTreeMap<&str, i32> = BTreeMap::from([("a", 1), ("d", 4),("b", 2), ("c", 3)]);
//3.插入元素
bm1.insert("zhangsan", 11);
bm1.insert("lisi", 22);
bm1.insert("wangwu", 33);
//4.获取元素
println!("get zhangsan: {}", bm1.get("zhangsan").unwrap());
println!("get lisi: {}", bm1.get("lisi").unwrap());
println!("get wangwu: {}", bm1["wangwu"]);
//5.遍历元素
//5.1 迭代器遍历
for (key, value) in bm2.iter() {
println!("iter key: {}, value: {}", key, value);
}
//5.2 迭代器可变引用遍历
for (key, val) in bm2.iter_mut() {
*val += 1;
println!("iter_mut key: {}, value: {}", key, val);
}
//5.3 迭代器范围遍历 遍历key的范围["a" 到 “c”]
for (key, value) in bm2.range("a"..="c") {
println!("range key={}, value={}", key, value);
}
//6.删除元素
bm2.remove("a");
//7.检查键是否存在
if bm2.contains_key("b") {
println!("bm2 contains key b");
}else{
println!("bm2 not contains key b");
}
}
输出信息:
7.HashSet使用方法
HashSet是基于HashMap实现的,即Key和Value的值都是一样的(实际上在Rust实现没有为Value分配空间),HashSet是一个集合,存放一组同一类型的数据,可以实现并集、交集、差集操作。
使用例子:
use std::collections::HashSet;
fn main() {
//1.使用HashSet::new()创建一个空的HashSet
let mut s1:HashSet<i32> = HashSet::new();
//2.使用HashSet::from()创建一个HashSet,使用数组进行初始化
let mut s2:HashSet<i32> = HashSet::from([1,2,3,4,5]);
//3.使用 vec! 宏和 into_iter 创建集合
let mut s3:HashSet<i32> = vec![2,3,4,9,8,7,6,5].into_iter().collect();
//4.使用HashSet::insert()插入数据
s1.insert(4);
s1.insert(6);
s1.insert(7);
//5.使用HashSet::remove()删除数据
s2.remove(&5);//&代表 引用
//6.使用take()方法获取并删除集合中的第一个元素
if let Some(v) = s3.take(&9){
println!("take 9:{}", v);
}
//7.使用HashSet::contains()检查集合中是否包含某个元素
if s3.contains(&6) {
println!("s3 contains 6");
}
//8.遍历元素
for val in s1.iter() {
println!("s1:{}", val);
}
// let v:Vec<i32> = s1.into_iter().collect();
//9.集合操作
//9.1 并集
let ss1: HashSet<i32> = s1.union(&s2).cloned().collect();
println!("s1={:?} s2={:?} s1与s2并集={:?}", s1, s2, ss1);
//9.2 交集
let ss2:HashSet<i32> = s2.intersection(&s3).cloned().collect();
println!("s2={:?} s3={:?} s1与s2交集={:?}", s2, s3, ss2);
//9.3 差集
let ss3:HashSet<i32> = s3.difference(&s1).cloned().collect();
println!("s3={:?} s1={:?} s3与s1差集={:?}", s3, s1, ss3);
}
输出信息:
8.BTreeSet使用方法
BTreeSet是基于BTreeMap实现的,即Key和Value的值都是一样的(实际上在Rust实现没有为Value分配空间),BTreeSet是一个集合,存放一组同一类型的数据,可以实现并集、交集、差集操作。与HashSet不同的是BTreeSet是有序集合,大家可以观察示例输出的信息验证。
使用示例:
use std::collections::BTreeSet;
fn main() {
//1.使用BTreeSet::new()创建一个空的BTreeSet
let mut s1:BTreeSet<i32> = BTreeSet::new();
//2.使用BTreeSet::from()创建一个BTreeSet,使用数组进行初始化
let mut s2:BTreeSet<i32> = BTreeSet::from([1,2,3,4,5]);
//3.使用 vec! 宏和 into_iter 创建集合
let mut s3:BTreeSet<i32> = vec![8,5,4,9,8,7].into_iter().collect();
//4.使用BTreeSet::insert()插入数据
s1.insert(4);
s1.insert(6);
s1.insert(7);
//5.使用BTreeSet::remove()删除数据
s2.remove(&5);//&代表 引用
//6.使用take()方法获取并删除集合中的第一个元素
if let Some(v) = s3.take(&9){
println!("take 9:{}", v);
}
//7.使用HashSet::contains()检查集合中是否包含某个元素
if s3.contains(&6) {
println!("s3 contains 6");
}
//8.遍历元素
for val in s1.iter() {
println!("s1:{}", val);
}
// let v:Vec<i32> = s1.into_iter().collect();
//9.集合操作
//9.1 并集
let ss1: BTreeSet<i32> = s1.union(&s2).cloned().collect();
println!("s1={:?} s2={:?} s1与s2并集={:?}", s1, s2, ss1);
//9.2 交集
let ss2:BTreeSet<i32> = s2.intersection(&s3).cloned().collect();
println!("s2={:?} s3={:?} s1与s2交集={:?}", s2, s3, ss2);
//9.3 差集
let ss3:BTreeSet<i32> = s3.difference(&s1).cloned().collect();
println!("s3={:?} s1={:?} s3与s1差集={:?}", s3, s1, ss3);
}
输出信息:
9.BinaryHeap使用方法
BinaryHeap 是 Rust 标准库中的一个数据结构,它实现了二叉堆(Binary Heap),默认是一个最大堆(Max-Heap)。二叉堆是一种特殊的完全二叉树,其中每个父节点的值都大于或等于其子节点的值(最大堆),或者每个父节点的值都小于或等于其子节点的值(最小堆)。
使用示例:
use std::collections::BinaryHeap;
fn main() {
//1.使用BinaryHeap::new()创建一个空的BinaryHeap
let mut bh1:BinaryHeap<i32, > = BinaryHeap::new();
//2.使用BinaryHeap::from()创建,使用数组进行初始化
let mut bh2:BinaryHeap<i32> = BinaryHeap::from([1,2,3,4,5]);
//3.使用BinaryHeap::push()插入数据
bh1.push(4);
bh1.push(7);
bh1.push(6);
bh1.push(3);
//4.使用BinaryHeap::peek()获取堆顶元素,但不删除
if let Some(v) = bh1.peek() {
println!("堆顶元素 bh1 peek:{}", v);
}
//5.使用BinaryHeap::pop()删除数据
if let Some(v) = bh1.pop() {
println!("删除数据bh1 pop:{}", v);
}
//6.遍历元素
for val in bh2.iter() {
println!("遍历bh2:{}", val);
}
}
输出信息:
10.小结
本篇以示例的方式展示Rust中容器数据结构的使用方式,便于快速的了解基本容器的使用方式。
我是小C,欢迎大家一起学习交流~~~