一、布隆過濾器是什么
布隆過濾器(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; } } }