概述
布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進制向量和一系列隨機映射函數,布隆過濾器可以用于檢索一個元素是否在一個集合中。
如果想要判斷一個元素是不是在一個集合里,一般想到的是將所有元素保存起來,然后通過比較確定。鏈表,樹等等數據結構都是這種思路. 但是隨著集合中元素的增加,我們需要的存儲空間越來越大,檢索速度也越來越慢(O(n),O(logn))。不過還有一種叫作散列表(又叫哈希表,Hash table)的數據結構,它可以通過一個Hash函數將一個元素映射成一個位陣列中的一個點,這樣一來,我們只要看看這個點是不是1就可以知道集合中有沒有它了。這就是布隆過濾器的基本思想。
算法
1、首先需要k個hash函數,每個函數可以把key散列成為1個整數;
2、初始化時,需要一個長度為n比特的數組,每個比特位初始化為0;
3、某個key加入集合時,用k個hash函數計算出k個散列值,并把數組中對應的比特位置為1;
4、判斷某個key是否在集合時,用k個hash函數計算出k個散列值,并查詢數組中對應的比特位,如果所有的比特位都是1,認為在集合中;

原理
布隆過濾器需要的是一個位數組(這個和位圖有點類似)和k個映射函數(和Hash表類似),在初始狀態時,對于長度為m的位數組array,它的所有位都被置為0,如下圖所示:

對于有n個元素的集合S={s1,s2......sn},通過k個映射函數{f1,f2,......fk},將集合S中的每個元素sj(1<=j<=n)映射為k個值{g1,g2......gk},然后再將位數組array中相對應的array[g1],array[g2]......array[gk]置為1:

如果要查找某個元素item是否在S中,則通過映射函數{f1,f2.....fk}得到k個值{g1,g2.....gk},然后再判斷array[g1],array[g2]......array[gk]是否都為1,若全為1,則item在S中,否則item不在S中。這個就是布隆過濾器的實現原理。
布隆過濾器優點
它的優點是空間效率和查詢時間都遠遠超過一般的算法,布隆過濾器存儲空間和插入 / 查詢時間都是常數O(k)。另外, 散列函數相互之間沒有關系,方便由硬件并行實現。布隆過濾器不需要存儲元素本身,在某些對保密要求非常嚴格的場合有優勢。

布隆過濾器缺點
但是布隆過濾器的缺點和優點一樣明顯。誤算率是其中之一。隨著存入的元素數量增加,誤算率隨之增加。但是如果元素數量太少,則使用散列表足矣。
另外,一般情況下不能從布隆過濾器中刪除元素. 我們很容易想到把位數組變成整數數組,每插入一個元素相應的計數器加 1, 這樣刪除元素時將計數器減掉就可以了。然而要保證安全地刪除元素并非如此簡單。首先我們必須保證刪除的元素的確在布隆過濾器里面. 這一點單憑這個過濾器是無法保證的。另外計數器回繞也會造成問題
如何選擇哈希函數個數和布隆過濾器長度
過小的布隆過濾器很快所有的 bit 位均為 1,那么查詢任何值都會返回“可能存在”,起不到過濾的目的了。布隆過濾器的長度會直接影響誤報率,布隆過濾器越長其誤報率越小。
哈希函數的個數也需要權衡,個數越多則布隆過濾器 bit 位置位 1 的速度越快,且布隆過濾器的效率越低;但是如果太少的話,那我們的誤報率會變高

k 為哈希函數個數,m 為布隆過濾器長度,n 為插入的元素個數,p 為誤報率。
應用場景
- HTTP緩存服務器、Web爬蟲等
主要工作是判斷一條URL是否在現有的URL集合之中(可以認為這里的數據量級上億)。
對于HTTP緩存服務器,當本地局域網中的PC發起一條HTTP請求時,緩存服務器會先查看一下這個URL是否已經存在于緩存之中,如果存在的話就沒有必要去原始的服務器拉取數據了,這樣既能節省流量,還能加快訪問速度,以提高用戶體驗。
對于Web爬蟲,要判斷當前正在處理的網頁是否已經處理過了,同樣需要當前URL是否存在于已經處理過的URL列表之中。
- 垃圾郵件過濾
假設郵件服務器通過發送方的郵件域或者IP地址對垃圾郵件進行過濾,那么就需要判斷當前的郵件域或者IP地址是否處于黑名單之中。如果郵件服務器的通信郵件數量非常大(也可以認為數據量級上億),那么也可以使用Bloom Filter算法。
JAVA實現布隆過濾器
先實現一個簡單的布隆過濾器


這段代碼是構建了一個10億位的bitSet,然后把一億個userId加入到了我們的布隆過濾器中,最近判斷5324512515這個userId是否登錄,打出代碼的執行時間
