本文主要结合开源lucene谈谈整形值的压缩问题,共分为基于内存的压缩和基于磁盘的压缩,此篇为基于内存的压缩。数组
假如需将一堆正整数保存在一个集合中,而且可以任意的读写,通常最容易想到的方法是建立一个整形数组或者链表进行保存,但直接保存的缺点在于浪费空间,好比给定100个1-10之间的数只需100*4bit=400bit的空间(10最多须要4个bit),正常须要100*32bit=3200bit的空间,这正是lucene的实现方式。所以通常在压缩前须要算出这一串数字中最大值占用的bit位,而后经过时空的权衡选择合适的实现方法。由于不一样的位数可能会跨数据块,如8位、16位、24位、32位、48位、64位能够经过现有的数据类型byte、short、int、long的组合进行表达;5位、11位等等就须要跨long或者int了,好比long能够存12个5bit的值,其他的4位必须和下一个long的1位进行合并这就是所谓的跨块,跨块后速度必定是有影响的,因此lucene给了跨块和不跨块的实现方式。若是不跨块就会浪费空间,这就是时空权衡。函数
Direct八、Direct1六、Packed8ThreeBlocks、Direct3二、Packed16ThreeBlocks、Direct6四、Packed64这几个类是没有空间浪费的,实际上Packed64就知足实现需求了,底层基于long数组,经过计算设置数值的位置和一些位运算进行实现,但Direct八、Direct1六、Packed8ThreeBlocks、Direct3二、Packed16ThreeBlocks、Direct64能够直接用byte、short、int、long进行表示,它的位置计算量比Packed64来的高效。继承
Packed64SingleBlock与Packed64相似,可是它不跨块,是空间换时间的一种实现。支持的数据位是1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 16, 21, 32这几种,缘由是用这些位数能够尽可能减小浪费的空间。假设支持11位那么浪费9bit(64-5*11=9),13位浪费12bit(64-13*4=12),其他你们能够本身算一算,总之浪费的空间确实大了点。内存
GrowableWriter可在压缩的过程当中动态计算值的最大bit位,而且动态扩充,它在某些场景下好比:数据集很大没法一下求出最大值占用的bit位,是颇有用的。it
PagedGrowableWriter在GrowableWriter基础上扩充了分段能力,即将数据集进行分段压缩好比1-16在段1,17-32在段2等等。一样对于GrowableWriter的使用场景,若是集合中有一个特大的值,那么全部的值都必须按照该值的bit位进行存储,很明显这也是浪费空间的。table
PagedMutable与PagedGrowableWriter相似,提供了分段压缩,可是每段不会随着数值位数进行自动扩充,因此本质上跟Direct八、Direct1六、Packed8ThreeBlocks、Direct3二、Packed16ThreeBlocks、Direct6四、Packed64和Packed64SingleBlock相似,只是分段后能够存储更多的值。基础
接下来讲一说PackedLongValues,这个类提供随机读,可是不提供随机写,与PagedGrowableWriter很类似,在添加数值的时候内部会统计最大值所占的bit位,而后分段存储。数据类型
DeltaPackedLongValues是继承于PackedLongValues的,若是数值比较接近会将全部值减去最小值后而后再压缩,这样能够进一步减小占用空间。lucene
MonotonicLongValues创建在DeltaPackedLongValues的基础上当给定序列是一个具备仿射函数序列时有较好的压缩效果,直接一点就是近似于一个线性函数序列时,首先经过求解函数的斜率将序列“放平”,获得一个平稳的序列以后,而后经过DeltaPackedLongValues方法进行二次压缩。方法
须要注意的是当数值bit位达到64的时候,能够存储负数了。