本文主要結合開源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的時候,能夠存儲負數了。