引子
首先通過一道題來理解什么是bitmap。
題目:我有40億個整數,再給一個新的整數,我需要判斷新的整數是否在40億個整數中,你會怎么做?
分析:
假設一個int占4個字節(32位),40個億個整數就是160億個字節,大概相當於16GB,假設一台計算機只有2GB內存,則16GB一次加載不完,需要分8次加載,從磁盤加載數據是磁盤io操作,是非常慢的(比內存中的操作要慢100倍),每次加載這么大的數據,並且要8次,那么查找的時間可以達到分鍾甚至小時級。
還有一個辦法是把數據分散在8台計算機上,然后來一個新數據,8台計算機同時找,然后再匯總結果。這樣每台計算機都可以一次性把數據讀入內存中,查找就不用來回加載數據,省去了加載數據的開銷,是個好方法。
但是否還有其他更好的方法呢?那就是bitmap。bitmap存值的思路:每一個int有32位,int整數的范圍是-2147483648 ~ 2147483647。為簡化理解,這里先假設每一個整數位均為正整數(如果存在負整數需分開處理),2147483647/32 = 67108863,即只需要67108863個int型整數就可以表示 [0,2147483647] 范圍的數字,即需要67108863*4 = 268,435,452個字節的內存,相當於0.2GB,即使加上負整數部分也才需要0.4GB的內存,一台計算機完全足夠。這里將開辟67108863int型數組,數組中的每一位代表依次代表 [0,2147483647]。而且而且判斷新的整數也只需要O(1)的時間復雜度,性能非常高。
bitmap定義
位圖是一個數組的每一個數據的每一個二進制位表示一個數據,0表示數據不存在,1表示數據存在。
例如存儲136這個數:
- 確定136在整個數據的那個區間,136/32 = 4,即在第四個區間;
- 確定136在這個區間的第幾位(bit),136%32 = 25,即在第四區間的第25位上;
- 將這個位置置為1,表示存在這個數。
由於bitmap的數據存儲方式,具有升序排序的性質。
代碼實現
#include <iostream> #include <vector> using namespace std; class Bitmap { public: Bitmap() { bitVec_.resize((INT_MAX >> 5) + 1); //多開辟一個空間,原因是數組只能表示區間[0,size) } void BitmapSet(int val) { int index = val >> 5; //相當於除以32,用移位操作可提高性能 int offset = val % 32; bitVec_[index] |= (1 << offset); int capacity = bitVec_.capacity(); } bool BitmapGet(int val) { int index = val >> 5; int offset = val % 32; return bitVec_[index] & (1 << offset); } private: vector<unsigned int> bitVec_; }; int main() { Bitmap bm; //這里只存[0,1000000]的數, for (int i = 0; i <= 1000000; ++i) { bm.BitmapSet(i); } bool exist1 = bm.BitmapGet(100); // 100是否存在,返回true bool exist2 = bm.BitmapGet(10000000); // 10000000是否存在,返回false system("pause"); return 0; }
上面實現只是針對開篇題目寫的簡單bitmap,下一篇文章會探討Bloom Filter的原理,會對用到的bitmap進行優化修改。