4个版本 (2个破坏性版本)
0.3.1 | 2023年12月2日 |
---|---|
0.3.0 | 2023年7月25日 |
0.2.0 | 2023年6月21日 |
0.1.0 | 2023年5月3日 |
#339 在 数据结构
370 每月下载
在 3 crate 中使用
48KB
1K SLoC
alot
一组用于使用生成的唯一键在类似映射的结构中存储值的集合。基本集合类型 Lots<T>
为每个存储的值返回一个 LotId
。可以使用这些 LotId
来检索或删除存储的值。
此集合提供的插入和读取性能与 Vec<T>
相当,但不保证包含值的顺序。
如果需要排序,提供 OrderedLots<T>
,它跟踪元素的顺序,同时仍然允许通过 LotId
进行查找。通过 LotId
删除值成为此集合的 O(n) 操作。
Lots<T>
:T的无序集合
use alot::Lots;
let mut map = Lots::new();
// Similar to a Vec, push adds a new value to the collection.
let greeting = map.push("hello, world");
// Prints: Greeting: LotId { generation: 1, index: 0 }
println!("Greeting: {greeting:?}");
// Values can be retrieved by their LotId.
assert_eq!(map[greeting], "hello, world");
OrderedLots<T>
:T的有序集合
use alot::OrderedLots;
let mut map = OrderedLots::new();
// Values stored in OrderedLots can be accessed by index or by their LotId.
let c = map.push("c");
let a = map.push("a");
assert_eq!(map[c], map[0]);
// With OrderedLots, values can be inserted as well as pushed.
let b = map.insert(1, "b");
assert_eq!(map, &["c", "b", "a"]);
// OrderedLots also provides sorting functions.
map.sort();
assert_eq!(map, &["a", "b", "c"]);
这个crate与其它crate有什么不同?
有几种“槽映射”或“代际竞技场”或其他类似名称的结构方法。这个crate采用了两种独特的方法
- 没有不安全代码。
LotId
是一个单一的usize
。大多数槽映射使用usize
作为索引,并使用另一个usize
作为代。- 在内部,每个值的存储空间只有最多2字节的开销,不包括编译器可能添加的填充。大多数代际映射必须存储一个
usize
作为代,并且许多由于使用Option<T>
而产生额外的1字节开销。 - 空闲列表是一个
Vec<usize>
,而不是尝试重用空槽的空间。这样做是为了以下优势- 在没有64位架构上的 unsafe 的情况下,如果没有造成
SlotData
枚举比目前在使用size_of::<T>
时占用更多空间的情况下,是无法将48位的索引数据放入Empty状态的。例如,Lots<u16>
的内部槽存储每个值使用4个字节。 - 除非收集被清空或进行大量删除操作,否则空闲列表通常较短。
- 在没有64位架构上的 unsafe 的情况下,如果没有造成
这些设计选择导致了此实现的一些限制
- 集合限制在最大
usize
的75%。通常,这并不是一个真正的限制,因为分配一个跨越目标架构RAM 75%的连续内存区域并不实际。在64位平台上,Lots<T>
可以容纳2^48个项目 - 281万亿个项目。 - 与使用完整的
usize
为代数的实现相比,此实现由于代数的旋转,更有可能返回过时的ID数据。 - 在大多数情况下,集合先变得很大然后又变得非常小,将比使用链表方法跟踪空闲槽的替代解决方案使用更多的RAM。