Immutable解读
louzhedong opened this issue · comments
Immutable解读
什么是Immutable
Immutable实现的原理是Persistent Data Structur(持久化数据结构),对Immutable对象的任何修改或添加删除操作都会返回一个新的Immutable对象。在用旧数据创建新数据时,保证旧数据同时可以且不可变。
为了提升性能,Immutable不会复制所有节点,而是使用Structural Sharing,如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其他节点则进行共享。如下图所示:
Vector Trie
Immutable参考了Vector Trie这种数据结构
Vector Trie 是这样一种数据结构:
假如我们有一个 map ,key 全都是数字 {0: ‘banana’, 1: ‘grape’, 2: ‘lemon’, 3: ‘orange’, 4: ‘apple’}
,为了构造一棵二叉Vector Trie
,我们可以先把所有的key转换为二进制的形式:{‘000’: ‘banana’, ‘001’: ‘grape’, ‘010’: ‘lemon’, ‘011’: ‘orange’, ‘100’: ‘apple’}
,这样就可以构建如下图Vector Trie
在树中,Vector Trie
的每个节点是一个数组,数组里有0
和1
两个数,表示一个二进制数,所有值都存在叶子节点上,比如我们要找001
的值时,只需顺着0
0
1
找下来,即可得到grape
。那么想实现持久化数据结构
当然也不难了,比如我们想添加一个5: ‘watermelon’
,添加之后的结构会变成如下:
蓝色的部分是新增的部分,黑色的部分直接引用之前的节点。这样添加一个元素,只会改变很少的节点,大部分节点都可以直接复用。这就大大提高了创建节点的效率。
时间复杂度
因为采用了结构共享,在修改时不需要将map中所有值都拷贝一遍,提升了效率。
但是查询的速度却变慢了,因为在map中,查询的时间复杂度是O(1),但在这里变成了一棵树,查询的时间复杂度变成了O(logN)。
树高压缩
假设有这么一个2叉Vector Trie,现在存了一个值,key为110,它是按如下图的方式存放的:
如果现在只存了这一个值,其实中间的节点都可以被忽略掉,其实就是如下所示:
当我们要添加新的节点时,再增加或减少中间的节点,如下所示:
节点内部压缩——Bitmap
在前面说的Trie树中,每个节点数组长度是32,但大部分情况下是用不到这么多节点的,这个时候我们可以使用bitmap对数组进行压缩,拿长度为8的数组举例:
其中只用到了2个节点,其实我们只需要指明数组中的哪些节点有值就好了
这样就更进一步地压缩了代码