BloomFilter 簡介
當一個元素被加入集合時,通過K個散列函數將這個元素映射成一個位數組中的K個點,把它們置為1。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有它了:如果這些點有任何一個0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。
優點:相比於其它的數據結構,布隆過濾器在空間和時間方面都有巨大的優勢。布隆過濾器存儲空間和插入/查詢時間都是常數(O(k))。而且它不存儲元素本身,在某些對保密要求非常嚴格的場合有優勢。
缺點:一定的誤識別率和刪除困難。
要使用BloomFilter,需要引入guava包:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
測試分兩步:
1、往過濾器中放一百萬個數,然后去驗證這一百萬個數是否能通過過濾器
2、另外找一萬個數,去檢驗漏網之魚的數量
/** * 測試布隆過濾器(可用於redis緩存穿透) * * @author xwj */ public class TestBloomFilter { private static int total = 1000000; private static BloomFilter<Integer> bf = BloomFilter.create(Funnels.integerFunnel(), total); // private static BloomFilter<Integer> bf = BloomFilter.create(Funnels.integerFunnel(), total, 0.001); public static void main(String[] args) { // 初始化1000000條數據到過濾器中 for (int i = 0; i < total; i++) { bf.put(i); } // 匹配已在過濾器中的值,是否有匹配不上的 for (int i = 0; i < total; i++) { if (!bf.mightContain(i)) { System.out.println("有壞人逃脫了~~~"); } } // 匹配不在過濾器中的10000個值,有多少匹配出來 int count = 0; for (int i = total; i < total + 10000; i++) { if (bf.mightContain(i)) { count++; } } System.out.println("誤傷的數量:" + count); } }
運行結果:
運行結果表示,遍歷這一百萬個在過濾器中的數時,都被識別出來了。一萬個不在過濾器中的數,誤傷了320個,錯誤率是0.03左右。
看下BloomFilter的源碼:
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, int expectedInsertions) { return create(funnel, (long) expectedInsertions); } public static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions) { return create(funnel, expectedInsertions, 0.03); // FYI, for 3%, we always get 5 hash functions } public static <T> BloomFilter<T> create( Funnel<? super T> funnel, long expectedInsertions, double fpp) { return create(funnel, expectedInsertions, fpp, BloomFilterStrategies.MURMUR128_MITZ_64); }
static <T> BloomFilter<T> create(
Funnel<? super T> funnel, long expectedInsertions, double fpp, Strategy strategy) {
......
}
BloomFilter一共四個create方法,不過最終都是走向第四個。看一下每個參數的含義:
funnel:數據類型(一般是調用Funnels工具類中的)
expectedInsertions:期望插入的值的個數
fpp 錯誤率(默認值為0.03)
strategy 哈希算法(樓主也不懂啥意思)
在最后一個create方法中,設置一個斷點:
上面的numBits,表示存一百萬個int類型數字,需要的位數為7298440,700多萬位。理論上存一百萬個數,一個int是4字節32位,需要4*8*1000000=3200萬位。如果使用HashMap去存,按HashMap50%的存儲效率,需要6400萬位。可以看出BloomFilter的存儲空間很小,只有HashMap的1/10左右
上面的numHashFunctions,表示需要5個函數去存這些數字
使用第三個create方法,我們設置下錯誤率:
private static BloomFilter<Integer> bf = BloomFilter.create(Funnels.integerFunnel(), total, 0.0003);
再運行看看:
此時誤傷的數量為4,錯誤率為0.04%左右。
當錯誤率設為0.0003時,所需要的位數為16883499,1600萬位,需要12個函數
和上面對比可以看出,錯誤率越大,所需空間和時間越小,錯誤率越小,所需空間和時間約大