这几篇想简单谈谈一下自己在写代码时遇见的,或者阅读 llvm 相关代码时见到的数据结构实现。
本文源代码:https://github.com/yunwei37/immutable-map-rs
关于 ImmutableMap
ImmutableMap 是一种可持久化数据结构,在进行插入或删除操作时并不对原先的数据结构进行改动,而是创建一个新的拷贝。关于可持久化数据结构,可以参考维基百科[1]:Persistent_data_structure
这里参考的是 llvm 中的 ImmutableMap/ImmutableSet 实现,采用一个平衡因子为 2 的 AVL 树[2]:
ImmutableSet is an immutable (functional) set implementation based on an AVL tree. Adding or removing elements is done through a Factory object and results in the creation of a new ImmutableSet object. If an ImmutableSet already exists with the given contents, then the existing one is returned; equality is compared with a FoldingSetNodeID. The time and space complexity of add or remove operations is logarithmic in the size of the original set.
There is no method for returning an element of the set, you can only check for membership.
ImmutableSet 是基于 AVL 树的不可变(功能)集实现。添加或删除元素是通过 Factory 对象完成的,并导致创建新的 ImmutableSet 对象。如果具有给定内容的 ImmutableSet 已经存在,则返回现有的;通过与 FoldingSetNodeID 进行比较判断是否相等。添加或删除操作的时间和空间复杂度与原始集合的大小成对数。
没有返回集合元素的方法,您只能检查元素是否存在。
关于 llvm 中 ImmutableSet 的原理和源代码实现,可以参考:clang static analyzer中的数据结构及内存分配策略 - ImmutableMap & ImmutableSet篇
感觉 llvm 里面实现的非常漂亮。
用 rust 实现
之所以要用 rust 写,很大一个方面是因为我很久没写 rust 了,需要重新复健一下(x),另外也是增加一点理解。
rust 的所有权模型实际上非常适合写这种不可变数据结构,比可变的 AVL tree 实现起来要方便和直观地多。另外,使用引用计数智能指针虽然会带来一些额外的开销,但实际上极大地减轻了内存管理的压力。借由 RC
甚至可以把它当成可变 AVL 树来使用,比如:
let map = ImmutableMap::new();
let map = map.insert(1, "abc");
let map = map.delete(1, "abc");
let size = new_map.size();
let data = new_map.get_val