rust 集合类型的使用

204 阅读6分钟

动态数组 Vector

Rust语言中的动态数组或者说可变数组,对应的是Vec<T>类型,动态数组只能存储相同类型的元素。这种类型在Rust中被广泛使用,允许我们在运行时动态地添加和删除元素。

Vec<T>在Rust中是一个堆分配的,动态大小的数组。它的内部实现是一个环形缓冲区,当达到容量限制时,它会自动进行重新分配,增长到更大的内存区域。

创建 Vec

  1. Vec::new(): 创建一个空的Vec<T>
	let mut vec = Vec::new(); // 创建一个空的Vec

如果预先知道要存储的元素个数,可以使用 Vec::with_capacity(capacity) 创建动态数组,这样可以避免因为插入大量新数据导致频繁的内存分配和拷贝,提升性能。

  1. vec![]

还可以使用宏 vec! 来创建数组,与 Vec::new 有所不同,这个能在创建同时给予初始化值:

let v = vec![1, 2, 3];

处的 v 也无需标注类型,编译器只需检查它内部的元素即可自动推导出 v 的类型是 Vec<i32> (Rust 中,整数默认类型是 i32)。

添加元素

  1. push(): 添加元素到Vec<T>中。
	let mut vec = Vec::new();  

	vec.push(1); // 添加一个元素到Vec中  
	vec.push(2);  
	vec.push(3);
  1. insert(): 在指定位置插入一个元素。
    let mut vec = Vec::new();
    vec.push(1);
    vec.push(2);
    vec.insert(1, 10); // 在索引为1的位置插入元素10  
    println!("{:?}", vec);  //[1, 10, 2]  

删除元素

  1. remove(): 删除Vec<T>中的元素。该方法会返回被删除的元素。
	let mut vec = vec![1, 2, 3];  

	let num = vec.remove(1); // 删除索引为1的元素,保存到num中
  1. swap_remove() : 删除并返回指定索引的元素,同时将该索引的元素与最后一个元素交换位置。
	let mut vec = vec![1, 2, 3];  

	let num = vec.swap_remove(1); // 删除并返回索引为1的元素,同时与最后一个元素交换位置,保存到num中
  1. clear(): 清除Vector中的所有元素。
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
vec.push(3);
vec.clear(); // 清空vec  
println!("{:?}", vec);  // []  

访问元素

你可以使用索引来访问Vec<T>中的元素。请注意Rust的索引是从0开始的。

	let vec = vec![1, 2, 3];  

	let first_num = vec[0]; // 访问第一个元素,应为1

如果你希望访问最后一个元素,可以使用last()方法。

	let vec = vec![1, 2, 3];  

	let last_num = vec.last(); // 获取最后一个元素,应为3

使用 get 方法

v.get 的使用方式非常安全,它在内部做了处理,有值的时候返回 Some(T),无值的时候返回 None,避免了数组越界导致的报错退出。

let v = vec![1, 2, 3, 4, 5];

let does_not_exist = &v[100]; //因为发生了数组越界访问,导致程序报错退出
let does_not_exist = v.get(100);

对 Vec 进行迭代

你可以通过迭代器来遍历Vec<T>中的所有元素。如下例:

	let vec = vec![1, 2, 3];  

	for num in &vec {  

	    println!("{}", num); // 在循环中打印每个元素  

	}

或者使用 iter_mut() 和 into_iter() 来遍历并可能修改元素:

	let mut vec = vec![1, 2, 3];  

	for num in vec.iter_mut() {  

	    *num += 1; // 修改每个元素的值  

	}  

	let vec = vec![2, 3, 4]; // Vec现在包含修改后的元素

其他

  1. len(): 返回Vector中元素的数量。
let vec = Vec::new();
vec.push(1);
vec.push(2);
vec.push(3);
println!("Number of elements: {}", vec.len()); // Number of elements: 3  
  1. is_empty(): 检查Vector是否为空。
let vec = Vec::new();
if vec.is_empty() { // 如果vec为空,打印一条信息  
    println!("Vector is empty."); // Vector is empty.  
} else { // 如果vec不为空,打印另一条信息  
    println!("Vector is not empty."); // Vector is not empty.  
}
  1. capacity(): 返回Vector的当前容量(即可以存储的元素数量)。
let mut vec = Vec::new();
vec.push(1); // 向vec中添加一个元素,容量增加到1(因为内部已经分配了一个空间)
println!("Current capacity: {}", vec.capacity()); // 输出:Current capacity: 1,因为内部已经分配了一个空间。

KV 存储 HashMap

Rust语言中的 KV 存储 HashMap 对应的是 std::collections::HashMap。这是一个强大的数据结构,允许你存储键值对,其中键是唯一的。你可以使用这个数据结构来存储和管理各种类型的数据。

HashMap 在Rust中是用于存储键值对的数据结构,类似于其他编程语言中的哈希表或字典。它提供了存储,查找,删除等操作。

创建 HashMap

  1. 使用 new 方法创建

创建和插入一个HashMap的示例:

	use std::collections::HashMap;  

	fn main() {  
	    // 创建一个新的HashMap  
	    let mut map = HashMap::new();  

	    // 插入键值对  
	    map.insert(String::from("key1"), String::from("value1"));  
	    map.insert(String::from("key2"), String::from("value2"));  
	    map.insert(String::from("key3"), String::from("value3"));  

	    // 打印出所有的键值对  
	    for (key, value) in &map {  
	        println!("{}: {}", key, value);  
	    }  
}

在上述代码中,首先导入了std::collections::HashMap,然后在main函数中创建了一个新的HashMap。然后,我们使用insert方法向哈希表中插入键值对。在循环中,我们遍历了HashMap中的所有键值对,并打印了它们。

跟 Vec 一样,如果预先知道要存储的 KV 对个数,可以使用 HashMap::with_capacity(capacity) 创建指定大小的 HashMap,避免频繁的内存分配和拷贝,提升性能。

  1. 使用迭代器和 collect 方法创建

在Rust中,你可以使用迭代器(Iterator)和collect方法来创建一个HashMap,而不使用for循环。以下是示例代码,展示了如何将Vec<(String, u32)>中的数据快速写入到HashMap<String, u32>中:

use std::collections::HashMap;

fn main() {
    let data: Vec<(String, u32)> = vec![
        ("key1".to_string(), 1),
        ("key2".to_string(), 2),
        ("key3".to_string(), 3),
    ];

    // 使用迭代器和collect方法创建HashMap
    let hashmap: HashMap<_,_> = data.into_iter().collect();

    // 打印HashMap中的值
    println!("{:?}", hashmap); // {"key3": 3, "key2": 2, "key1": 1}
}

在这个示例中,首先创建了一个包含键值对的Vec,然后通过调用into_iter 方法将列表转为迭代器。
通过使用collect方法,我们可以将迭代器中的元素收集到一个新的HashMap中。最后,我们打印出HashMap中的值。
HashMap<_,_>的 KV 类型,会由编译器帮我们推导出HashMap<String, u32>

  1. 有则更新,无则插入新值
use std::collections::HashMap;

fn main() {
    let text = "a b c a";

    let mut map = HashMap::new();
    // 根据空格来切分字符串
    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }

    println!("{:?}", map); // {"c": 1, "a": 2, "b": 1}
}

在这个示例中,新建一个 map 用于保存词语出现的次数,插入一个词语时会进行判断:若之前没有插入过,则使用该词语作 Key,插入次数 0 作为 Value,若之前插入过则取出之前统计的该词语出现的次数,对其加一。

注意:

  • or_insert 返回了 &mut v 引用,因此可以通过该可变引用直接修改 map 中对应的值
  • 使用 count 引用时,需要先进行解引用 *count,否则会出现类型不匹配
  1. HashMap也提供了其他的方法,例如:
  • get(key: K) -> Option<&V>:获取一个键对应的值。
    • 如果这个键不存在,返回None。否则,返回一个指向值的引用。
    • &V 是对 HashMap 中值的借用,如果不使用借用,可能会发生所有权的转移
  • remove(key: K) -> Option<V>:删除一个键值对并返回它的值。如果这个键不存在,返回None