RoaringBitmap数据结构及原理
来源:互联网 发布:域名注册空间商网站 编辑:程序博客网 时间:2024/06/03 15:16
首先
每个RoaringBitmap(GitHub链接)中都包含一个RoaringArray
,名字叫highLowContainer
。 highLowContainer
存储了RoaringBitmap
中的全部数据。
RoaringArray highLowContainer;
这个名字意味着,会将32位的整形(int
)拆分成高16位和低16位两部分(两个short
)来处理。
RoaringArray的数据结构很简单,核心为以下三个成员:
short[] keys;Container[] values;int size;
每个32位的整形,高16位会被作为key存储到short[] keys
中,低16位则被看做value,存储到Container[] values
中的某个Container中。keys
和values
通过下标一一对应。size
则标示了当前包含的key-value pair的数量,即keys
和values
中有效数据的数量。
keys
数组永远保持有序,方便二分查找。
三种Container
下面介绍到的是RoaringBitmap的核心,三种Container。
通过上面的介绍我们知道,每个32位整形的高16位已经作为key存储在RoaringArray
中了,那么Container只需要处理低16位的数据。
ArrayContainer
static final int DEFAULT_MAX_SIZE = 4096short[] content;
结构很简单,只有一个short[] content
,将16位value直接存储。
short[] content
始终保持有序,方便使用二分查找,且不会存储重复数值。
因为这种Container存储数据没有任何压缩,因此只适合存储少量数据。
ArrayContainer占用的空间大小与存储的数据量为线性关系,每个short
为2字节,因此存储了N个数据的ArrayContainer占用空间大致为2N
字节。存储一个数据占用2字节,存储4096个数据占用8kb。
根据源码可以看出,常量DEFAULT_MAX_SIZE
值为4096,当容量超过这个值的时候会将当前Container替换为BitmapContainer。
BitmapContainer
final long[] bitmap;
这种Container使用long[]
存储位图数据。我们知道,每个Container处理16位整形的数据,也就是0~65535,因此根据位图的原理,需要65536个比特来存储数据,每个比特位用1来表示有,0来表示无。每个long
有64位,因此需要1024个long
来提供65536个比特。
因此,每个BitmapContainer在构建时就会初始化长度为1024的long[]
。这就意味着,不管一个BitmapContainer中只存储了1个数据还是存储了65536个数据,占用的空间都是同样的8kb。
RunContainer
private short[] valueslength;int nbrruns = 0;
RunContainer中的Run指的是行程长度压缩算法(Run Length Encoding),对连续数据有比较好的压缩效果。
它的原理是,对于连续出现的数字,只记录初始数字和后续数量。即:
- 对于数列
11
,它会压缩为11,0
; - 对于数列
11,12,13,14,15
,它会压缩为11,4
; - 对于数列
11,12,13,14,15,21,22
,它会压缩为11,4,21,1
;
源码中的short[] valueslength
中存储的就是压缩后的数据。
这种压缩算法的性能和数据的连续性(紧凑性)关系极为密切,对于连续的100个short
,它能从200字节压缩为4字节,但对于完全不连续的100个short
,编码完之后反而会从200字节变为400字节。
如果要分析RunContainer的容量,我们可以做下面两种极端的假设:
- 最好情况,即只存在一个数据或只存在一串连续数字,那么只会存储2个
short
,占用4字节 - 最坏情况,0~65535的范围内填充所有的奇数位(或所有偶数位),需要存储65536个
short
,128kb
Container性能总结
读取时间
只有BitmapContainer可根据下标直接寻址,复杂度为O(1)
,ArrayContainer和RunContainer都需要二分查找,复杂度O(log n)
内存占用
这是我画的一张图,大致描绘了各Container占用空间随数据量的趋势。
其中,
- ArrayContainer一直线性增长,在达到4096后就完全比不上BitmapContainer了
- BitmapContainer是一条横线,始终占用8kb
- RunContainer比较奇葩,因为和数据的连续性关系太大,因此只能画出一个上下限范围。不管数据量多少,下限始终是4字节;上限在最极端的情况下可以达到128kb。
RoaringBitmap针对Container的优化策略
创建时:
- 创建包含单个值的Container时,选用ArrayContainer
- 创建包含一串连续值的Container时,比较ArrayContainer和RunContainer,选取空间占用较少的
转换:
针对ArrayContainer:
- 如果插入值后容量超过4096,则自动转换为BitmapContainer。因此正常使用的情况下不会出现容量超过4096的ArrayContainer。
- 调用runOptimize()方法时,会比较和RunContainer的空间占用大小,选择是否转换为RunContainer。
针对BitmapContainer:
- 如果删除某值后容量低至4096,则会自动转换为ArrayContainer。因此正常使用的情况下不会出现容量小于4096的BitmapContainer。
- 调用runOptimize()方法时,会比较和RunContainer的空间占用大小,选择是否转换为RunContainer。
针对RunContainer:
- 只有在调用runOptimize()方法才会发生转换,会分别和ArrayContainer、BitmapContainer比较空间占用大小,然后选择是否转换。
以上
- RoaringBitmap数据结构及原理
- roaringBitMap
- java数据结构及原理
- EWAHCompressedBitmap数据结构及原理
- RoaringBitmap简析
- Java数据结构及原理实现
- MySQL:数据结构及算法原理
- JAVA常用数据结构及原理分析
- MySQL索引数据结构及算法原理
- 索引背后的数据结构及算法原理
- 数据结构 线索二叉树 原理及实现
- Java HashMap 实现原理及数据结构
- Java HashSet 实现原理及数据结构
- JAVA常用数据结构及原理分析
- 【数据结构】ArrayList原理及实现学习总结
- 【数据结构】LinkedList原理及实现学习总结
- 【数据结构】HashMap原理及实现学习总结
- 【数据结构】HashSet原理及实现学习总结
- UBUNTU上安装花生壳PHDDNS
- 【巨杉案例】银行高并发柜面账单查询案例
- mysql从库重建
- MVC5与EF6 Code First 第一个入门完整实例教程
- 欢迎使用CSDN-markdown编辑器
- RoaringBitmap数据结构及原理
- Codeforces Round #438 (Div. 1 + Div. 2 combined)(除G)解题报告
- .\与..\的区别
- Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
- 使用CEfSharp之旅(3)下载文件 弹出保存框 IDownloadHandler
- BP算法经典例题 longest increasing subsequence
- numpy.linspace使用详解...
- String、StringBuffer与StringBuilder之间区别
- 数据结构项目二:停车场管理