簡化布隆過濾器——BitMap

簡化布隆過濾器——BitMap

前言

前段開發項目試就發現,一部分的代碼實現存在着一些性能上的隱患。但當時忙於趕進度和因爲卡發中的不穩定因素,想了許多解決方案也沒有機會實施。最近,正好趁個機會進行一系列的改進。java

我在團隊開發中負責開發服務器端。因此在編寫業務邏輯層時,經常遇到如下這樣的業務邏輯:
1. 判斷一個用戶是否爲在本身的好友列表中
2. 判斷一條動態是否已被用戶翻閱
3. 判斷兩個用戶的標籤的匹配度
4. .....等等
這些狀況,我以前的方案是採用數據庫來解決,爲每條記錄添加標記,須要查詢時則遍歷返回相應的集合數據庫

可是隨着用戶量的不斷增多、各個用戶之間的關係不斷地增長、以及用戶使用軟件的一系列行爲中這些狀況是很是頻繁的,這樣頻繁遍歷大量的記錄的讀操做會給數據庫帶來難以承受的壓力。
數組

那麼如何需找一種更好的解決方案?
既能減小數據庫須要遍歷的記錄數量且快速索引,又能用少許的內存表示大量的數據。
其實若是咱們對這一類型的業務邏輯進行抽象,能夠獲得:本質上就是判斷一個元素是否存在於集合中
因此咱們能夠採用位數組,經過數組的下標能快速地定位某個元素,用bit表示相應的內容可以節省大量的空間。
服務器

可是這樣結構依舊不夠完美,若是數據量相對較少,數組中會存在大量的無用數據, 如長度爲1024的byte數組中的只有少許位被表示爲1,大量位依然是0。
此時咱們能夠採用遊程編碼壓縮byte數組。如上圖的遊程編碼後的結果能夠表示爲[3, 0, 2, 0, 2, 0, 1, 0 ]數據結構

1、Bitmap介紹

Bitmap:被設計爲一種用bit數組來儲存表示2種狀態緊湊、快速索引的數據結構(固然Java的util包中也實現這類型的數據結構—BitSet(不過並非Set))app

2、BitMap主要原理

其實說開來,Bitmap就是一個位數組而已,有着快速訪問優點(下標訪問),以及極小佔用(用1bit來表示)post

3、BitMap的主要設計

有點美中不足的是,Java中並無提供bit這樣的數據類型,即使是最小的數據類型byte也要佔用8bit。這樣就須要進行一些位運算來完成相應的操做,使得代碼變得稍微複雜。
1. BitMap的內部經過byte數組實現
2. BitMap的基本操做:增刪改查性能

void  Set(int  position);      /* 將某位置"1" */
boolean Get(int position); /* 判斷某位的值 */
void Clear(int postion); /* 將某位置"0" */

Set()的實現原理

廢話很少說,直接看圖

主要分爲兩個步驟:
1. 先將一個byte類型的」1」左移4位,獲得結果
2. 再進行簡單的或運算,獲得結果並覆蓋原來的值ui

Get()的實現原理


理解了上面的例子,相信這個應該就很簡單了
一樣是兩步:
1. 先將一個byte類型的」1」左移3位,獲得結果
2. 再進行與操做,獲得結果並覆蓋原來的值this

或許這裏會有些疑問,爲何不考慮用boolean?
首先,Java規範中沒有強制規定boolean所佔內存的大小。並且大部分計算機容許分配的最小內存單元爲8bit

4、能夠用運用BitMap解決問題的實用場景

大多能夠運用的場景主要是兩個方面:
這裏以標籤匹配爲例子,開發中一個用戶與各個用戶之間的標籤匹配度是使人頭疼的問題,經過匹配標籤字符串或者標籤ID,這樣的效果都不能太讓人滿意,在數據庫中的保存也頗爲麻煩。

1、快速索引

假如,每一個用戶都有一個這樣小小的長度爲40的byte數組,那麼用戶就能夠用它來表示320種標籤。並且可以快速的查詢,經過bitarray[tag_id]這樣的訪問方式能夠極快查到,用戶是否選取了這個標籤,可以快速地計算與各個用戶之間的標籤匹配度

2、數據壓縮

那麼像第一點說的那樣,長度爲40的byte數據即可以保存320種標籤信息,但它內存大小隻有40B。並且這仍是沒有進行遊程編碼壓縮以前的大小

5、Java實現

/**
 * Created by auhnayuil on 17-6-7.
 */
public class BitMap {

    public static final int DEFAULT_SIZE = 1024;

    public static final boolean EXIST = true;

    public static final boolean NULL = false;

    public static final short bits = 8;

    private byte[] bitArray;

    private int size;

    public BitMap(){
        this(DEFAULT_SIZE);
    }

    public BitMap(byte[] bitArray){
        this.size = bitArray.length * bits;
        this.bitArray = bitArray;
    }

    public BitMap(int defaultSize) {
        this.size = defaultSize * bits;
        this.bitArray = new byte[defaultSize];
    }

    public BitMap(int size, boolean elem){
        this(size);

        if(EXIST == elem) {
            for (int i = 0; i < bitArray.length; i++)
                bitArray[i] = (byte) ~bitArray[i];
        }
    }

    public int size(){
        return size;
    }

    public int index(int position){
        int idx = (position + bits - 1) / bits; 
        return idx - 1;
    }

    public int offset(int position){
        int ofs = position % 8;
        return (ofs == 0 ? ofs : 8 - ofs);
    }

    public void setBit(int position){
        if(position > size)
            return ;

        int idx = index(position);
        int ofs = offset(position);
        bitArray[idx] |= (byte)(1 << ofs);
    }

    public boolean getBit(int position){
        if(position > size)
            return false;

        int idx = index(position);
        int ofs = offset(position);
        byte tmp = (byte)(bitArray[idx] & (1 << ofs));

        return tmp != 0;
    }

    public void setBitArray(byte[] bitArray){
        this.bitArray = bitArray;
    }

    public byte[] getBitArray(){
        return bitArray;
    }

    public String byteToStr(int position){
        byte b = bitArray[index(position)];

        StringBuffer sb = new StringBuffer("");
        for(int i=bits-1; i>-1; i--)
            sb.append((byte)((b >> i) & 0x1));
        return sb.toString();
    }
}
相關文章
相關標籤/搜索