Bootstrap

Rust标准库中集合类型用法详解


在Rust中Vec、HashMap和HashSet是非常常用的集合类型,它们是通过标准库std::vec和std::collections模块提供的。

Vec: 用于存储动态数组,支持增、删、查、改等操作,适用于元素顺序访问。
HashMap<K, V>: 存储键值对的哈希表,提供高效的查找、插入、删除操作,适用于映射关系。
HashSet: 不允许重复元素的哈希集合,支持高效的插。

这里介绍一下这三个集合类型的用法。

Vec 动态数组

Vec是Rust中用于存储可变大小、同类型元素的集合类型。Vec是一个堆分配的数据结构,允许动态添加或删除元素。

创建动态数组

使用Vec::new()来创建一个空的Vec。
使用vec![]宏来创建并初始化一个Vec。

//创建一个空的Vec
let mut v: Vec<i32> = Vec::new();
//使用宏创建并初始化Vec
let v2 = vec![1, 2, 3, 4]; // 类型推导为 Vec<i32>

增加删除元素

push(): 将元素添加到 Vec 的末尾。
pop(): 从 Vec 中移除并返回最后一个元素。
insert(): 在指定位置插入元素。
remove(): 移除指定位置的元素。
drain(): 删除指定范围的元素。
split_off(): 以指定位置索引为标志,分割数组。

//添加和移除元素
let mut v = Vec::new();
//向Vec添加元素
v.push(1);   
v.push(2);
//移除最后一个元素
println!("Last element: {:?}", v.pop()); 
println!("Vec after pop: {:?}", v);
//在位置1插入元素10  
v.insert(1, 10);
println!("Vec after insert: {:?}", v);
//移除索引为0的元素
v.remove(0);  

let mut v1 = vec![11, 22, 33, 44, 55];
//删除指定范围的元素,同时获取被删除元素的迭代器
let mut m: Vec<_> = v1.drain(1..=3).collect();   
//指定索引处切分成两个 vec, m: [22], v2: [33, 44]
let v2 = m.split_off(1);        

访问元素

使用索引或切片来访问Vec中的元素。
使用get()方法来安全地访问元素,避免数组越界。

let v = vec![10, 20, 30, 40];
println!("First element: {}", v[0]); // 使用索引访问
//使用get方法它返回一个 Option
match v.get(1) {
    Some(value) => println!("Second element: {}", value),
    None => println!("No element at this index"),
}

遍历Vec

可以使用for循环、iter()方法等来遍历Vec。

let v = vec![1, 2, 3, 4];
for val in &v {
    println!("{}", val); // 值的借用
}

for val in v.iter() {
    println!("{}", val); // 使用 iter()
}

控制容量

调整动态数组的容量大小。

//指定空间  
let mut v = Vec::with_capacity(10);
v.extend([1, 2, 3]); 
//调整空间  
v.reserve(100);
//释放剩余的容量,一般情况下,不会主动去释放容量
v.shrink_to_fit();     

修改元素

可以通过索引或iter_mut()来修改元素。

let mut v = vec![1, 2, 3, 4];
//使用索引修改元素
v[2] = 30;
println!("Vec after modification: {:?}", v);
//使用iter_mut()修改元素
for val in &mut v {
    *val *= 2; // 通过引用修改值
}
println!("Vec after multiplying by 2: {:?}", v);

元素排序

在rust里实现了两种排序算法,分别为稳定的排序sort和sort_by,以及非稳定排序sort_unstable和sort_unstable_by。
非稳定并不是指排序算法本身不稳定,而是指在排序过程中对相等元素的处理方式。在稳定排序算法里,对相等的元素,不会对其进行重新排序。而在不稳定的算法里则不保证这点。总体而言非稳定排序的算法的速度会优于稳定排序算法,同时稳定排序还会额外分配原数组一半的空间。

//整数元素排序  
fn main() {
    let mut vec = vec![1, 5, 10, 2, 15];    
    vec.sort_unstable();    
    assert_eq!(vec, vec![1, 2, 5, 10, 15]);
}

在浮点数当中存在一个NAN值,这个值无法与其他的浮点数进行对比,因此浮点数类型并没有实现全数值可比较Ord的特性,而是实现了部分可比较的特性PartialOrd。如此,如果我们确定在我们的浮点数数组当中,不包含NAN值,那么我们可以使用partial_cmp来作为大小判断的依据。

fn main() {
    let mut vec = vec![1.0, 5.6, 10.3, 2.0, 15f32];    
    vec.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());    
    assert_eq!(vec, vec![1.0, 2.0, 5.6, 10.3, 15f32]);
}

定义自定义排序方法对结构体进行排序

//实现 Ord、Eq、PartialEq、PartialOrd 这些属性 便可以使用默认排序  不需要自定义了
/*
#[derive(Debug, Ord, Eq, PartialEq, PartialOrd)]
struct Person {
    name: String,
    age: u32,
}*/
#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
}
impl Person {
    fn new(name: String, age: u32) -> Person {
        Person { name, age }
    }
}
fn main() {
    let mut people = vec![
        Person::new("Zoe".to_string(), 25),
        Person::new("Al".to_string(), 60),
        Person::new("John".to_string(), 1),
    ];
    //定义一个按照年龄倒序排序的对比函数
    people.sort_unstable_by(|a, b| b.age.cmp(&a.age));
}

HashMap<K, V>哈希表

HashMap是一个存储键值对(key-value)的集合类型,提供了通过键(key)高效查找、插入和删除值(value)的能力。

创建 HashMap

使用HashMap::new()创建一个空的HashMap。
使用hashmap![]宏来创建并初始化一个HashMap。

use std::collections::HashMap;

let mut map = HashMap::new();
map.insert("name", "Alice");
map.insert("age", "30");

// 使用宏创建并初始化 HashMap
// 如果你希望使用hashmap!宏,确保你导入了maplit库,并在代码中使用#[macro_use]来启用宏。
let mut map2 = hashmap! {
    "name" => "Bob",
    "age" => "25",
};

插入和更新元素

insert(key, value): 将键值对插入到HashMap中,如果键已存在则更新对应的值。

let mut map = HashMap::new();
map.insert("name", "Alice");
map.insert("age", "30");

// 插入一个已存在的键,值会被更新
map.insert("age", "31");
println!("Updated map: {:?}", map);

访问元素

使用get()方法安全地查找键对应的值返回Option。
使用索引语法map[key]会在键不存在时panic。

let map = HashMap::from([("name", "Alice"), ("age", "30")]);

// 使用 get() 方法返回 Option
match map.get("name") {
    Some(value) => println!("Name: {}", value),
    None => println!("No value found for key 'name'"),
}

// 使用索引访问元素(可能会 panic)
println!("Age: {}", map["age"]);

删除元素

remove(): 根据键删除键值对,返回被删除的值。

let mut map = HashMap::new();
map.insert("name", "Alice");
map.insert("age", "30");

map.remove("age");
println!("After removal: {:?}", map);

遍历HashMap

可以使用for循环来遍历HashMap。

let map = HashMap::from([("name", "Alice"), ("age", "30")]);

for (key, value) in &map {
    println!("Key: {}, Value: {}", key, value);
}

使用默认值

可以使用entry()方法与or_insert()方法来为不存在的键插入默认值。

let mut map = HashMap::new();
map.entry("name").or_insert("Alice");
map.entry("age").or_insert("30");
println!("{:?}", map);

HashSet哈希集合

HashSet是一个不允许重复元素的集合类型,基于哈希表实现。它提供了高效的插入、删除和查找操作。

创建 HashSet

使用 HashSet::new() 创建一个空的 HashSet。
使用 hashset![] 宏来创建并初始化一个 HashSet。

use std::collections::HashSet;

let mut set = HashSet::new();
set.insert(1);
set.insert(2);
set.insert(3);

// 使用宏创建并初始化 HashSet
let set2: HashSet<_> = [1, 2, 3].iter().cloned().collect();

插入和删除元素

insert(): 向集合中插入元素,如果该元素已存在,则不会插入。
remove(): 从集合中移除元素。

let mut set = HashSet::new();
set.insert(1);
set.insert(2);
set.insert(2); // 重复的元素不会被插入

set.remove(&1);
println!("Set after removal: {:?}", set);

查找元素

使用contains()方法检查集合中是否包含某个元素。

let set: HashSet<_> = [1, 2, 3].iter().cloned().collect();

if set.contains(&2) {
    println!("Set contains 2");
} else {
    println!("Set does not contain 2");
}

遍历HashSet

可以使用for循环来遍历HashSet。

let set: HashSet<_> = [1, 2, 3].iter().cloned().collect();

for val in &set {
    println!("{}", val);
}

使用集合的并集、交集等操作

HashSet提供了集合运算的方法如并集、交集、差集等。

use std::collections::HashSet;

let set1: HashSet<_> = [1, 2, 3].iter().cloned().collect();
let set2: HashSet<_> = [3, 4, 5].iter().cloned().collect();

// 并集
let union: HashSet<_> = set1.union(&set2).cloned().collect();
println!("Union: {:?}", union);

// 交集
let intersection: HashSet<_> = set1.intersection(&set2).cloned().collect();
println!("Intersection: {:?}", intersection);
;