先看一個場景:如何判斷一個數是否在40億個整數中?
題目:給一臺普通PC,2G內存。我有40億個整數,再給一個新的整數,我需要判斷新的整數是否在40億個整數中,你會怎麼做?
分析:
40億個int佔(40億*4字節)/1024/1024/1024 大概爲16G左右,很明顯內存只有2G,放不下,因此不可能將這40億數據放到內存中計算。要快速的解決這個問題最好的方案就是將數據擱內存了,所以現在的問題就在如何在2G內存空間以內存儲着40億整數。
判斷一個數是否存在,其實只有兩個狀態,存在或者不存在,可以用一個位代表。這樣我就申請40億個位就好了,新的數轉換成一個位,然後判斷一下這個位是0還是1就行了。其實你可以想想,32位int的範圍,總共就是2的32次方,大概42億多點。所以你可以申請2的32次方個位。算一下需要多少內存呢?2的32次方個位,相當於2的29次方個字節,哇,才500MB,真是節省了不少內存呢。
BitMap思想:
一個byte是佔8個bit,如果每一個bit的值就是有或者沒有,也就是二進制的0或者1,如果用bit的位置代表數組值有還是沒有,那麼0代表該數值沒有出現過,1代表該數組值出現過。不也能描述數據了嗎?如下圖:
再看代碼之前,我們先搞清楚一個問題,一個數怎麼快速定位它的索引號,也就是說搞清楚byte[index]的index是多少,position是哪一位。舉個例子吧,例如add(14)。14已經超出byte[0]的映射範圍,在byte[1]範圍之類。那麼怎麼快速定位它的索引呢。如果找到它的索引號,又怎麼定位它的位置呢。Index(N)代表N的索引號,Position(N)代表N的所在的位置號。
Index(N) = N/8 = N >> 3;
Position(N) = N%8 = N & 0x07;
基於上面的分析,我們寫一個簡單的BitMap的算法如下:
package bitmap;
public class BitMap {
//保存數據的
private byte[] bits;
//能夠存儲多少數據
private int capacity;
public BitMap(int capacity){
this.capacity = capacity;
//1bit能存儲8個數據,那麼capacity數據需要多少個bit呢,capacity/8+1,右移3位相當於除以8
bits = new byte[(capacity >>3 )+1];
}
public void add(int num){
// num/8得到byte[]的index
int arrayIndex = num >> 3;
// num%8得到在byte[index]的位置
int position = num & 0x07;
//將1左移position後,那個位置自然就是1,然後和以前的數據做|,這樣,那個位置就替換成1了。
bits[arrayIndex] |= 1 << position;
}
public boolean contain(int num){
// num/8得到byte[]的index
int arrayIndex = num >> 3;
// num%8得到在byte[index]的位置
int position = num & 0x07;
//將1左移position後,那個位置自然就是1,然後和以前的數據做&,判斷是否爲0即可
return (bits[arrayIndex] & (1 << position)) !=0;
}
public void clear(int num){
// num/8得到byte[]的index
int arrayIndex = num >> 3;
// num%8得到在byte[index]的位置
int position = num & 0x07;
//將1左移position後,那個位置自然就是1,然後對取反,再與當前值做&,即可清除當前的位置了.
bits[arrayIndex] &= ~(1 << position);
}
public static void main(String[] args) {
BitMap bitmap = new BitMap(100);
bitmap.add(7);
System.out.println("插入7成功");
boolean isexsit = bitmap.contain(7);
System.out.println("7是否存在:"+isexsit);
bitmap.clear(7);
isexsit = bitmap.contain(7);
System.out.println("7是否存在:"+isexsit);
}
}