Scala的Vector如何工作?

时间:2013-12-16 13:59:51

标签: scala data-structures

我读了this page关于Scala集合的时间复杂性。如上所述,Vector的复杂性为eC,适用于所有操作。

这让我想知道Vector是什么。我看了document并说:

  

因为向量在快速随机选择和快速随机功能更新之间取得了良好的平衡,所以它们目前是   不可变索引序列的默认实现。它得到了支持   一个小端点位映射矢量trie,分支因子为32。   地方非常好,但不是连续的,这是非常好的   大序列。

与Scala的其他内容一样,它非常模糊。 Vector实际上是如何工作的?

3 个答案:

答案 0 :(得分:34)

此处的关键字为Trie。 Vector实现为Trie数据结构。 请参阅http://en.wikipedia.org/wiki/Trie

更确切地说,它是“位映射矢量trie”。我刚刚在这里找到了一个足够的结构描述(以及一个实现 - 显然在Rust中):

https://bitbucket.org/astrieanna/bitmapped-vector-trie

最相关的摘录是:

  

Bitmapped Vector Trie基本上是一个32树。级别1是大小为32的数组,无论数据类型如何。 2级是32级1级的数组。等等,直到:等级7是2级6的数组。

更新:回复赖玉轩关于复杂性的评论:

我将不得不假设你的意思是“深度”:-D。 “eC”的图例说“操作需要有效的恒定时间,但这可能取决于一些假设,例如矢量的最大长度或散列键的分布。”

如果您愿意考虑最坏的情况,并且考虑到向量的最大大小存在上限,那么确实可以说复杂性是恒定的。 假设我们认为最大大小为2 ^ 32,那么这意味着最坏的情况是最多7次操作,无论如何。 然后,我们总是可以考虑任何类型的集合的最坏情况,找到一个上限,并说这是恒定的复杂性,但对于列表的例子,这将意味着一个40亿的常数,这是不太实际的。 / p>

但是Vector是相反的,7个操作更实用,这就是我们在实践中考虑其复杂性常数的方式

另一种看待这种情况的方法:我们不是在讨论log(2,N),而是log(32,N)。如果你试图绘制,你会发现它实际上是一条水平线。如此务实地说,随着集合的增长,你将永远无法看到处理时间的大幅增加。 是的,那仍然不是真正的常数(这就是为什么它被标记为“eC”而不仅仅是“C”),你将能够看到短矢量之间的差异(但同样,因为数字的差异非常小)运营增长太慢了。)

答案 1 :(得分:17)

其他答案“特里”很好。但作为近似,只是为了快速理解:

  • Vector内部使用树结构 - 不是二叉树,而是32-ary树
  • 每个'32 -way节点'使用Array [32]并且可以存储 0-32引用子节点 0-32条数据
  • 树的结构是以某种方式平衡 - 它是“n”级别的深度,但是级别1到n-1是“仅索引级别”(100%子引用;没有数据),级别n包含所有数据(100%数据;没有子引用)。因此,如果数据元素的数量是“d”,那么n = log-base-32(d)舍入向上

为什么这样?简单:性能。

不是为每个单独的数据元素进行数千/数百万/千万亿的内存分配,而是在32个元素块中分配内存。结构非常浅,而不是走几英里才能找到你的数据 - 它是一棵非常宽阔的短树。例如。 5级深度可以包含32 ^ 5个数据元素(对于4字节元素= 132GB,即相当大)并且每个数据访问将查找&从根开始遍历5个节点(而大数组将使用单个数据访问)。向量不会主动为所有Level n(数据)分配内存, - 它根据需要分配32个元素块。它的读取性能有点类似于大型阵列,同时具有与二叉树有些相似的功能特性(功率和灵活性以及内存效率)。

:)

答案 2 :(得分:7)

这些对您来说可能很有意思: