一、Rust 集合
Rust 中的集合(Collection),大多存储在堆上,大小可以在运行时动态改变,不像数组那样需要在编译时就确定固定的大小。
它们都遵循 Rust 严格的所有权和借用规则,确保内存安全,无需担心常见的内存错误,如悬空指针、缓冲区溢出等。
这些集合涵盖了从简单的线性存储,到键值对映射,再到特殊用途的集合。
集合 | 说明 |
Vec<T> | 一个可增长的、在堆上分配的、T 类型值的数组 |
VecDeque<T> | 双端队列(可增长环形缓冲区), 先进先出队列。支持高效地在首部和尾部添加或移除元素 |
LinkedList<T> | 双向链表 |
BinaryHeap<T> where T: Ord | 优先队列,大顶堆, BinaryHeap中的值按照一定结构组织,因此总是可以高效地找到和移除最大值 |
HashMap<K, V> where K: Eq + Hash | 键值哈希表, 一个键值对的表,通过键查找值很快速 |
BTreeMap<K, V> where K: Ord | 有序键值表, 按键的顺序保持条目有序 |
HashSet<T> where T: Eq + Hash | 基于哈希的无序集合 |
BTreeSet<T> where T: Ord | 有序集合 |
二、动态数组 Vector
Vector,通常简称为 Vec,是 Rust 中最常用的集合之一。
Vector在内存中有三个字段:长度、容量、和一个指向堆上分配的缓冲区的指针。
1、Vec<T> 的定义:
2、Vec<T> 的创建:
3、访问元素
访问元素,有两种方式。 一种是使用索引,要小心越界问题。
Rust 不会自动检查,越界会导致程序 panic, 并且vector 的长度和索引都是usize 类型:
另一种更安全的方式是使用 get 方法,它返回一个 Option<&T>,如果索引无效则返回 None,避免了 panic。
4、遍历元素
vector 和切片可以以值或者以引用迭代,遵循IntoIterator 实现中介绍的模式。
数组、切片和vector 还有 .iter() 和 .iter_mut() 方法创建产生元素的引用的迭代器。
三、 双端队列 VecDeque
VecDeque(发音为 “vector deck”)是一个双端队列,它支持在头部和尾部高效地添加和移除元素.
1、VecDeque<T> 的定义:
很多Vec 的方法在VecDeque 中都有实现:.len(), .is_empty(), .insert(index, value), .remove(index), .extend(iterable),等等
2、VecDeque<T> 的使用:
3、VecDeque<T> 的实现
VecDeque 的实现是一个环形缓冲区。
类似于Vec,它有一个堆上分配的缓冲区用来存储元素。
不同的是,数据并不总是从区域的起点开始存储,并且可以在结尾处“回环”。
下图中的队列元素,按顺序分别是['A', 'B', 'C', 'D', 'E']。
VecDeque 的私有的字段标记图中的start 和stop,用来记录缓冲区中数据的起点和终点。
向队列首部或者尾部添加值,意味着要占用一个未使用的位置,如果需要的话可能会回环或者分配更大的内存块。
因为双端队列在内存中并不是连续存储元素,因此它不能继承切片的方法。 但VecDeque 提供了一个方法来修复它:
四、 优先队列 BinaryHeap
BinaryHeap 是一个优先队列 默认实现为最大堆(可以通过自定义实现最小堆),它保证每次弹出的元素都是堆中的最大(或最小)元素。
插入和弹出操作的时间复杂度为 O (log n)。
1、BinaryHeap<T> 的定义:
2、BinaryHeap<T> 的使用:
五、 HashMap<K, V> 和 BTreeMap<K, V>
map(映射) 是键值对(称为条目(entry))的集合。
任何两个条目的键都不同,所有的条目按照一定结构组织。
Rust 提供两者两种map 类型:HashMap<K, V> 和BTreeMap<K, V>。
这两种类型共享了很多相同的方法; 不同之处在于它们组织条目的方式。
1、HashMap<K, V> 与 BTreeMap<K, V> 的定义:
2、HashMap<K, V> 与 BTreeMap<K, V> 的使用:
3 HashMap<K, V> 与 BTreeMap<K, V> 的内存排布
(1) HashMap<K, V> 的内存排布
所有的键、值和缓存的哈希值都被存储在单个堆上分配的表中。
添加条目最终会迫使HashMap 分配更大的表,并把所有数据移动进去。
(2) BTreeMap<K, V> 的内存排布
BTreeMap 按照键的顺序在树形结构中存储条目,因此它要求键的类型K 实现了Ord
BTreeMap 把条目存储在节点(node) 中。
一个BTreeMap 中大多数的节点都只包含键值对,而非叶节点。
添加条目通常需要把某个节点的部分向现有条目向右移动,来保持它们有序,有时还需要分配新的节点。
六、 HashSet 和 BTreeSet
set 是值的集合,它可以快速地测试元素。
一个set 绝不会包含同一个值的多个拷贝。
HashSet 是一个无序的集合,它只存储不重复的元素,内部基于哈希表实现,保证插入、删除和查询的平均时间复杂度为 O (1)。
BTreeSet 是一个基于二叉搜索树实现的有序集合,它保证元素的插入、删除和查询操作的时间复杂度为 O (log n),且元素始终有序存储。
1、HashSet<T> 与 BTreeSet<T> 的定义:
2、HashSet<T> 与 BTreeSet<T> 的使用:
七、 链表 LinkedList
LinkedList 是一种链式存储的数据结构,每个节点包含数据和指向下一个节点的指针(在双向链表中还有指向前一个节点的指针)。
Rust 标准库中的 LinkedList 提供了丰富的操作方法。 LinkedList 很少使用,并且在大多数情况下都有更好的替代,无论是性能还是接口。
八、 哈希
Hash 是标准库用于可哈希类型的trait。
HashMap的键和HashSet的元素必须实现Hash 和Eq。
大多数实现了Eq 的内建类型也都实现了Hash。
整数类型、char、String 都是可哈希类型;
当元素是可哈希类型时,元组、数组、切片、vector 也是可哈希类型。
标准库的原则是无论一个值存储到哪里或者如何指向它,它必须有相同的哈希值。 因此,引用和被引用的值有相同的哈希值。
Box和被装箱的值有相同的哈希值。
一个vector和包含它的所有元素的切片&vec[..] 有相同的哈希值。
一个String和一个有相同字符的&str有相同的哈希值。
结构和枚举默认没有实现Hash,但可以派生实现:
只要所有的字段都是可哈希的就可以正常工作。
如果手动为一个类型实现了PartialEq,那也应该手动实现Hash。
有相同ID的两个Artifact被认为相等:
当比较两个Artifact时只根据ID 进行比较,因此必须用相同的方式哈希它们:
这样就可以创建一个Artifact 的HashSet: