淺談布隆過濾器


一、布隆過濾器是什么

  布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都比一般的算法要好的多,缺點是有一定的誤識別率和刪除困難。

二、布隆過濾器的基本思想
  通過一種叫作散列表(又叫哈希表,Hash table)的數據結構。它可以通過一個Hash函數將一個元素映射成一個位陣列(Bit array)中的一個點。這樣一來,我們只要看看這個點是不是1就可以知道集合中有沒有它了

三、布隆過濾器如何解決哈希沖突的

  解決方法就是使用多個Hash函數,如果它們有一個說元素不在集合中,那肯定就不在。如果它們都說在,雖然也有一定可能性它們在說謊,不過直覺上判斷這種事情的概率是比較低的。

四、布隆過濾器的優缺點

優點:

  1.布隆過濾器的存儲空間和插入/查詢時間都是常數

  2.Hash函數相互之間沒有關系,方便由硬件並行實現

  3.布隆過濾器不需要存儲元素本身,在某些對保密要求非常嚴格的場合有優勢

  4.布隆過濾器可以表示全集,其它任何數據結構都不能

缺點:

  1.存在一定的誤算率,隨着存入的元素數量增加,誤算率隨之增加

    (常見的補救辦法是建立一個小的白名單,存儲那些可能被誤判的元素。但是如果元素數量太少,則使用散列表足矣)

  2.一般情況下不能從布隆過濾器中刪除元素

    首先我們必須保證刪除的元素的確在布隆過濾器里面. 這一點單憑這個過濾器是無法保證的。另外計數器回繞也會造成問題。這就導致刪除元素需要很高的成本。

五、布隆過濾器的應用場景

  1.網頁URL的去重

  2.垃圾郵件的判別

  3.集合重復元素的判別

  4.查詢加速(比如基於key-value的存儲系統)等

六、布隆過濾器的JAVA實現

/**
  * 描述:TODO(用一句話描述該文件做什么)
  */
package com.zbq.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.BitSet;

/**
 * 類名: BloomFilterTest
 * 包名: com.utilTest
 * 描述: 布隆過濾器,傳統的布隆過濾器不支持從集合中刪除成員
 */
public class BloomFilterTest {
    //DEFAULT_SIZE為2的29次方,即此處的左移28位
    private static final int DEFAULT_SIZE = 2<<28;
    /*
     * 不同哈希函數的種子,一般取質數
     * seeds數組共有8個值,則代表采用8種不同的哈希函數
     */
    private int[] seeds = new int[]{3, 5, 7, 11, 13, 31, 37, 61};
    /*
     * 初始化一個給定大小的位集
     * BitSet實際是由“二進制位”構成的一個Vector。
     * 假如希望高效率地保存大量“開-關”信息,就應使用BitSet.
     */
    private BitSet bitSets = new BitSet(DEFAULT_SIZE);
    //構建hash函數對象
    private SimpleHash[] hashFuns = new SimpleHash[seeds.length];
    //布隆過濾器配置文件存放路徑
    private String path = "";

    public BloomFilterTest(String path){
        /**
         *  給出所有的hash值,共計seeds.length個hash值。共8位。
         *  通過調用SimpleHash.hash(),可以得到根據8種hash函數計算得出hash值。
         *  傳入DEFAULT_SIZE(最終字符串的長度),seeds[i](一個指定的質數)即可得到需要的那個hash值的位置。
         */
        for(int i=0; i<seeds.length; i++){
            hashFuns[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
        }
        //配置文件路徑地址
        this.path = path;
    }
    /**
     *
     * 方法名:add
     * 描述:將給定的字符串標記到bitSets中,即設置字符串的8個函數值的位置為1
     * @param value
     */
    public synchronized void add(String value){
        for(SimpleHash hashFun : hashFuns){
            bitSets.set(hashFun.hash(value), true);
        }
    }
    /**
     *
     * 方法名:isExit
     * 描述:判斷給定的字符串是否已經存在在bloofilter中,如果存在返回true,不存在返回false
     * @param value
     * @return
     */
    public synchronized boolean isExit(String value){
        //判斷傳入的值是否為null
        if(null == value){
            return false;
        }

        for(SimpleHash hashFun : hashFuns){
            if(!bitSets.get(hashFun.hash(value))){
                //如果判斷8個hash函數值中有一個位置不存在即可判斷為不存在Bloofilter中
                return false;
            }
        }

        return true;
    }

    /**
     *
     * 方法名:init
     * 描述:讀取配置文件
     */
    public void init(){
        File file = new File(path);
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
            long lt = System.currentTimeMillis();
            read(in);
            System.out.println(System.currentTimeMillis()-lt);
            System.out.println(Runtime.getRuntime().totalMemory());
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try {
                if(in!=null){
                    in.close();
                    in = null;
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    /**
     *
     * 方法名:read
     * 描述:根據傳入的流,初始化bloomfilter
     * @param in
     */
    private void read(InputStream in){
        if(null == in){    //如果in為null,則返回
            return;
        }

        int i = 0;
        InputStreamReader reader = null;

        try {
            //創建輸入流
            reader = new InputStreamReader(in, "UTF-8");
            BufferedReader buffReader = new BufferedReader(reader, 512);
            String theWord = null;
            do {
                i++;
                theWord = buffReader.readLine();
                //如果theWord不為null和空,則加入Bloomfilter中
                if(theWord!=null && !theWord.trim().equals("")){
                    add(theWord);
                }
                if(i%10000 == 0){
                    System.out.println(i);
                }

            } while (theWord != null);

        } catch (IOException e){
            e.printStackTrace();
        } finally{
            //關閉流
            try {
                if(reader != null){
                    reader.close();
                    reader = null;
                }
                if(in != null){
                    in.close();
                    in = null;
                }
            } catch (IOException e) {
                // TODO: handle exception
                e.printStackTrace();
            }

        }
    }

    /**
     * 方法名:main
     * 描述:TODO(這里用一句話描述這個方法的作用)
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        BloomFilterTest bloomFilterTest = new BloomFilterTest("f:/fetchedurls.txt");
        bloomFilterTest.init();

        System.out.println(bloomFilterTest.isExit("http://www.plating.org/news_info.asp?pid=28&id=2857"));
    }

    public static class SimpleHash {
        /*
         * cap為DEFAULT_SIZE,即用於結果的最大字符串的值
         * seed為計算hash值的一個key值,具體對應上文中的seeds數組
         */
        private int cap;
        private int seed;
        /**
         *
         * 構造函數
         * @param cap
         * @param seed
         */
        public SimpleHash(int cap, int seed){
            this.cap = cap;
            this.seed = seed;
        }
        /**
         *
         * 方法名:hash
         * 描述:計算hash的函數,用戶可以選擇其他更好的hash函數
         * @param value
         * @return
         */
        public int hash(String value){
            int result = 0;
            int length = value.length();
            for(int i=0; i<length; i++){
                result = seed*result + value.charAt(i);
            }

            return (cap-1) & result;
        }
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM