簡化布隆過濾器——bitmap

前言

前段開發(fā)項目試就發(fā)現(xiàn)丈莺,一部分的代碼實(shí)現(xiàn)存在著一些性能上的隱患哄孤。但當(dāng)時忙于趕進(jìn)度和由于卡發(fā)中的不穩(wěn)定因素悴势,想了許多解決方案也沒有機(jī)會實(shí)施钱贯。最近挫掏,正好趁個機(jī)會進(jìn)行一系列的改進(jìn)。

我在團(tuán)隊開發(fā)中負(fù)責(zé)開發(fā)服務(wù)器端秩命。所以在編寫業(yè)務(wù)邏輯層時尉共,常常遇到以下這樣的業(yè)務(wù)邏輯:
1. 判斷一個用戶是否為在自己的好友列表中
2. 判斷一條動態(tài)是否已被用戶翻閱
3. 判斷兩個用戶的標(biāo)簽的匹配度
4. .....等等
這些情況,我之前的方案是采用數(shù)據(jù)庫來解決弃锐,為每條記錄添加標(biāo)記袄友,需要查詢時則遍歷返回相應(yīng)的集合

但是隨著用戶量的不斷增多霹菊、各個用戶之間的關(guān)系不斷地增加剧蚣、以及用戶使用軟件的一系列行為中這些情況是非常頻繁的,這樣頻繁遍歷大量的記錄的讀操作會給數(shù)據(jù)庫帶來難以承受的壓力旋廷。

那么如何需找一種更好的解決方案鸠按?
既能減少數(shù)據(jù)庫需要遍歷的記錄數(shù)量且快速索引,又能用少量的內(nèi)存表示大量的數(shù)據(jù)饶碘。
其實(shí)如果我們對這一類型的業(yè)務(wù)邏輯進(jìn)行抽象目尖,可以得到:本質(zhì)上就是判斷一個元素是否存在于集合中
所以我們可以采用位數(shù)組,通過數(shù)組的下標(biāo)能快速地定位某個元素扎运,用bit表示相應(yīng)的內(nèi)容能夠節(jié)省大量的空間卑雁。

但是這樣結(jié)構(gòu)依舊不夠完美,如果數(shù)據(jù)量相對較少绪囱,數(shù)組中會存在大量的無用數(shù)據(jù)测蹲, 如長度為1024的byte數(shù)組中的只有少量位被表示為1,大量位依然是0鬼吵。
此時我們可以采用游程編碼壓縮byte數(shù)組扣甲。如上圖的游程編碼后的結(jié)果可以表示為[3, 0, 2, 0, 2, 0, 1, 0 ]

bitmap介紹

bitmap:被設(shè)計為一種用bit數(shù)組來儲存表示2種狀態(tài)緊湊、快速索引的數(shù)據(jù)結(jié)構(gòu)(當(dāng)然Java的util包中也實(shí)現(xiàn)這類型的數(shù)據(jù)結(jié)構(gòu)—BitSet(不過并不是Set))

bitmap主要原理

其實(shí)說開來,bitmap就是一個位數(shù)組而已琉挖,有著快速訪問優(yōu)勢(下標(biāo)訪問)启泣,以及極小占用(用1bit來表示)

bitmap的主要設(shè)計

有點(diǎn)美中不足的是,Java中并沒有提供bit這樣的數(shù)據(jù)類型示辈,即便是最小的數(shù)據(jù)類型byte也要占用8bit寥茫。這樣就需要進(jìn)行一些位運(yùn)算來完成相應(yīng)的操作,使得代碼變得稍微復(fù)雜矾麻。
1. bitmap的內(nèi)部通過byte數(shù)組實(shí)現(xiàn)
2. bitmap的基本操作:增刪改查

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

Set()的實(shí)現(xiàn)原理

廢話不多說纱耻,直接看圖



主要分為兩個步驟:

  1. 先將一個byte類型的"1"左移4位,得到結(jié)果
  2. 再進(jìn)行簡單的或運(yùn)算险耀,得到結(jié)果并覆蓋原來的值

Get()的實(shí)現(xiàn)原理


理解了上面的例子弄喘,相信這個應(yīng)該就很簡單了
同樣是兩步:

  1. 先將一個byte類型的"1"左移3位,得到結(jié)果
  2. 再進(jìn)行與操作甩牺,得到結(jié)果并覆蓋原來的值

或許這里會有些疑問蘑志,為什么不考慮用boolean?
首先贬派,Java規(guī)范中沒有強(qiáng)制規(guī)定boolean所占內(nèi)存的大小急但。而且大部分計算機(jī)允許分配的最小內(nèi)存單元為8bit

可以用運(yùn)用bitmap解決問題的實(shí)用場景

大多可以運(yùn)用的場景主要是兩個方面:
這里以標(biāo)簽匹配為例子,開發(fā)中一個用戶與各個用戶之間的標(biāo)簽匹配度是令人頭疼的問題搞乏,通過匹配標(biāo)簽字符串或者標(biāo)簽ID波桩,這樣的效果都不能太讓人滿意,在數(shù)據(jù)庫中的保存也頗為麻煩查描。

一突委、快速索引

假如柏卤,每個用戶都有一個這樣小小的長度為40的byte數(shù)組冬三,那么用戶就可以用它來表示320種標(biāo)簽。而且能夠快速的查詢缘缚,通過bitarray[tag_id]這樣的訪問方式可以極快查到勾笆,用戶是否選取了這個標(biāo)簽,能夠快速地計算與各個用戶之間的標(biāo)簽匹配度

二桥滨、數(shù)據(jù)壓縮

那么像第一點(diǎn)說的那樣窝爪,長度為40的byte數(shù)據(jù)便可以保存320種標(biāo)簽信息,但它內(nèi)存大小只有40B齐媒。而且這還是沒有進(jìn)行游程編碼壓縮之前的大小

Java實(shí)現(xiàn)

/**
 * Created by 某拆遷大隊的二哈 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();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蒲每,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子喻括,更是在濱河造成了極大的恐慌邀杏,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唬血,死亡現(xiàn)場離奇詭異望蜡,居然都是意外死亡唤崭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門脖律,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谢肾,“玉大人,你說我怎么就攤上這事小泉÷瑁” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵膏孟,是天一觀的道長眯分。 經(jīng)常有香客問我,道長柒桑,這世上最難降的妖魔是什么弊决? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮魁淳,結(jié)果婚禮上飘诗,老公的妹妹穿的比我還像新娘。我一直安慰自己界逛,他們只是感情好昆稿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著息拜,像睡著了一般溉潭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上少欺,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天喳瓣,我揣著相機(jī)與錄音,去河邊找鬼赞别。 笑死畏陕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的仿滔。 我是一名探鬼主播惠毁,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼崎页!你這毒婦竟也來了鞠绰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤飒焦,失蹤者是張志新(化名)和其女友劉穎蜈膨,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丈挟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年刁卜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曙咽。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛔趴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出例朱,到底是詐尸還是另有隱情孝情,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布洒嗤,位于F島的核電站箫荡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏渔隶。R本人自食惡果不足惜羔挡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望间唉。 院中可真熱鬧绞灼,春花似錦、人聲如沸呈野。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽被冒。三九已至军掂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昨悼,已是汗流浹背蝗锥。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工幔戏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留税课,地道東北人闲延。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓韩玩,卻偏偏與公主長得像垒玲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子找颓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器益老,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 10,934評論 6 13
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法寸莫,內(nèi)部類的語法捺萌,繼承相關(guān)的語法,異常的語法膘茎,線程的語...
    子非魚_t_閱讀 31,599評論 18 399
  • 前言 臺灣這幾年,隨時可見各種資方與勞方對立的新聞披坏。多年沒有調(diào)薪,連大學(xué)教師都出來爭取自己的權(quán)益棒拂。在資方的自我意識...
    高浩容閱讀 1,824評論 2 5
  • harrytc閱讀 215評論 0 0