1. 程式人生 > >BitMap演算法基本操作

BitMap演算法基本操作

上篇我們講了BitMap是如何對資料進行儲存的,沒看過的可以看一下 BitMap演算法介紹

這篇我們來講一下BitMap這個資料結構的程式碼實現。

回顧下資料的儲存原理

一個二進位制位對應一個非負數n,如果n存在,則對應的二進位制位的值為1,否則為0。

這個時候,我們的第一個問題:

我們在使用byte,int,short,long等這些資料型別在儲存資料的時候,他們最小的都要佔用一個位元組的記憶體,也就是8個bit,也就是說,最小的操作單位是8個bit。根本就沒有可以一個一個bit位操作的資料型別啊。

在Java的bitMaP實現中,它採用的是用一個long資料來進行儲存的。一個long佔用8個位元組,即64bit,所以一個long可以儲存64個數。例如 arr 是一個long 型別的陣列,則 arr[0]可以存 0 ~ 63,arr[1]可以存64 ~127,以此類推。

不過,我們就採用byte陣列的來存吧。一個byte佔用一個位元組,即8bit,可以存8個數字。

當然,你要採用long陣列來存也可以。在實現上可以說是一樣的。

例如我們要儲存(1,3,5,7,8,10)時,他們的記憶體如下所示。 

 


下面我們就來講講如何對一個一個位進行操作的。

如何向bitmap中新增一個數值

我們先來說說如何在bitmap中如何新增一個數值的問題,例如我們我們要新增n=14。

這個其實很簡單,我們先找到n在arr陣列中的下標index,顯然index = 1。然後再找到n在arr[index]中的位置position,顯然這裡position = 6。

這裡還是可以很容易找出index和position的公式的。即

index = n / 8 = n >> 3。

position = n % 8 = n & 0x07。

接下來我們把1向右移動position個二進位制位,然後把所得的結果和arr[index]做“或(or)”操作就可以了。如下圖

這裡有個需要注意的地方,在畫圖的時候,為了方便,我們是把左邊的位當作低位,右邊的位當作高位來算了。不過在實際的儲存中,左邊的才是存高位,而右邊的存的是低位。所以在我們的程式碼實現中,我們所說的右移對應程式碼的左移。

程式碼實現

//新增資料的操作

public void add(int n){

   //用>>的操作是,運算會比較快

   int index = n >> 3;

   int position = n & 0x07;

   //把1右移和做or操作兩步一起

   //即 << 對應上圖的右移,實際上<<是左移符。

   arr[index] |= 1 << position;

}

知道了add操作,其他的操作差不多類似。

當然,我們實現的add操作只是簡單的實現一下,假如你要嚴謹地實現的話,還是需要很多異常的判斷的。例如判斷這個數是否是非負數,判斷arr陣列是否下標越界,進行容量的擴充等等。有興趣的可以嚴謹去實現一下。

刪除操作

我們只需要把對應的二進位制的1變成0就可以了。

我們可以把1右移(程式碼中對應左移)後的結果取反,然後與arr[index]做“與”操作就可以了。程式碼如下:

public void delete(int n){

   int index = n >> 3;

   int position = n & 0x07;

   arr[index] &= ~(1 << position);

}

判斷是否存在操作

我們把1右移之後,把結果和arr[index]做“與”操作,如何結果不為0,則證明存在,否則就不存在。

public boolean contain(int n){

   int index = n >> 3;

   int position = n & 0x07;

   return (arr[index] & (1 << position)) != 0;

}

三個最基本的操作程式碼基本實現了。

全部程式碼

public class BitMap {

   private byte[] arr;

   //容量,即最多能夠存多少個數據

   private int capacity;

   public BitMap(int capacity) {

       this.capacity = capacity;

       //一個byte可以存8個數據,capacity實際上指的是多少個bit

       arr = new byte[(capacity / 8 + 1)];

   }

   //新增資料的操作

   public void add(int n){

       //用>>的操作是,運算會比較快

       int index = n >> 3;

       int position = n & 0x07;

       //把1右移和做or操作兩步一起

       //即 << 對應上圖的右移,實際上<<是左移符。

       arr[index] |= 1 << position;

   }

   public void delete(int n){

       int index = n >> 3;

       int position = n & 0x07;

       arr[index] &= ~(1 << position);

   }

   public boolean contain(int n){

       int index = n >> 3;

       int position = n & 0x07;

       return (arr[index] & (1 << position)) != 0;

   }

}

問題

大家看了以上的程式碼,有沒發現一些問題呢?

例如我們只在bitmap儲存1個數,並且存的數值是2000000000,我們就會在第2000000000個二進位制把0改為1。也就是說arr陣列的大小至少為2000000000/8+1。可是這時候前面的二進位制位並沒有存資料,那不是超級超級浪費資源?

所以說,像我們上面的那種寫法可以說是暴力寫法,沒有經過任何優化,實際上,在Java自帶的bitMap中是有很多優化的,並不會像我們上面實現的程式碼一樣那麼浪費空間資源。有興趣的可以研究下我的上一篇中的java寫法。