louzhedong / blog

前端基础,深入以及算法数据结构

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Immutable解读

louzhedong opened this issue · comments

Immutable解读

什么是Immutable

Immutable实现的原理是Persistent Data Structur(持久化数据结构),对Immutable对象的任何修改或添加删除操作都会返回一个新的Immutable对象。在用旧数据创建新数据时,保证旧数据同时可以且不可变。

为了提升性能,Immutable不会复制所有节点,而是使用Structural Sharing,如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其他节点则进行共享。如下图所示:

img

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的每个节点是一个数组,数组里有01两个数,表示一个二进制数,所有值都存在叶子节点上,比如我们要找001的值时,只需顺着0 0 1找下来,即可得到grape。那么想实现持久化数据结构当然也不难了,比如我们想添加一个5: ‘watermelon’,添加之后的结构会变成如下:

蓝色的部分是新增的部分,黑色的部分直接引用之前的节点。这样添加一个元素,只会改变很少的节点,大部分节点都可以直接复用。这就大大提高了创建节点的效率。

时间复杂度

因为采用了结构共享,在修改时不需要将map中所有值都拷贝一遍,提升了效率。

但是查询的速度却变慢了,因为在map中,查询的时间复杂度是O(1),但在这里变成了一棵树,查询的时间复杂度变成了O(logN)。

树高压缩

假设有这么一个2叉Vector Trie,现在存了一个值,key为110,它是按如下图的方式存放的:

如果现在只存了这一个值,其实中间的节点都可以被忽略掉,其实就是如下所示:

当我们要添加新的节点时,再增加或减少中间的节点,如下所示:

节点内部压缩——Bitmap

在前面说的Trie树中,每个节点数组长度是32,但大部分情况下是用不到这么多节点的,这个时候我们可以使用bitmap对数组进行压缩,拿长度为8的数组举例:

其中只用到了2个节点,其实我们只需要指明数组中的哪些节点有值就好了

这样就更进一步地压缩了代码