中國大學MOOC-陳越、何欽銘-資料結構-起步能力自測題
阿新 • • 發佈:2021-01-05
布隆過濾器
布隆過濾器(Bloom Filter)是一種來檢索元素是否在給定大集合中的資料結構,這種資料結構是高效且效能很好的,但缺點是具有一定的錯誤識別率和刪除難度。並且,理論情況下,新增到集合中的元素越多,誤報的可能性就越大。
它由位陣列
和一系列雜湊函式
構成。初始時位陣列的值全為0。
布隆過濾器原理
加入元素
當一個元素加入布隆過濾器中的時候,會進行如下操作:
- 使用布隆過濾器中的雜湊函式對元素值進行計算,得到雜湊值(有幾個雜湊函式得到幾個雜湊值)。
- 根據得到的雜湊值,在位陣列中把對應下標的值置為 1。
判斷元素是否存在
當我們需要判斷一個元素是否存在於布隆過濾器的時候,會進行如下操作:
- 使用布隆過濾器中的雜湊函式對該元素值進行計算;
- 得到多個雜湊值之後判斷位陣列中的對應為位置元素是否都為 1,如果值都為 1,那麼說明這個值在布隆過濾器中,如果存在一個值不為 1,說明該元素不在布隆過濾器中。
由於不同的元素值通過相同的雜湊函式可能為得到相同的雜湊值,所以布隆過濾器在判斷元素存在時為存在誤判的情況,但判斷元素不存在時是確定的。
使用場景
- 判斷給定資料是否存在於一個大量資料集合中:比如判斷一個數字是否存在於包含大量數字的數字集中(數字集很大,5億以上!)、 防止快取穿透(判斷請求的資料是否有效避免直接繞過快取請求資料庫)等等、郵箱的垃圾郵件過濾、黑名單功能等等。
- 去重:比如爬給定網址的時候對已經爬取過的 URL 去重。
Java 實現布隆過濾器
可以自己實現,也可以使用 Guava 提供的實現。
package top.zysite.bloom.filter;
import java.util.BitSet;
/**
* 布隆過濾器
*
* @author Leo
* @create 2020/12/25 15:12
**/
public class MyBloomFilter {
/**
* 位陣列的大小
*/
private static final int DEFAULT_SIZE = 2 << 24;
/**
* 通過這個陣列可以建立 6 個不同的雜湊函式
*/
private static final int[] SEEDS = new int[]{3, 13, 46, 71, 91, 134};
/**
* 位陣列。陣列中的元素只能是 0 或者 1
*/
private BitSet bits = new BitSet(DEFAULT_SIZE);
/**
* 存放包含 hash 函式的類的陣列
*/
private SimpleHash[] func = new SimpleHash[SEEDS.length];
/**
* 初始化多個包含 hash 函式的類的陣列,每個類中的 hash 函式都不一樣
*/
public MyBloomFilter() {
// 初始化多個不同的 Hash 函式
for (int i = 0; i < SEEDS.length; i++) {
func[i] = new SimpleHash(DEFAULT_SIZE, SEEDS[i]);
}
}
/**
* 新增元素到位陣列
*/
public void add(Object value) {
for (SimpleHash f : func) {
bits.set(f.hash(value), true);
}
}
/**
* 判斷指定元素是否存在於位陣列
*/
public boolean contains(Object value) {
boolean ret = true;
for (SimpleHash f : func) {
ret = ret && bits.get(f.hash(value));
}
return ret;
}
/**
* 靜態內部類。用於 hash 操作!
*/
public static class SimpleHash {
private int cap;
private int seed;
public SimpleHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
/**
* 計算 hash 值
*/
public int hash(Object value) {
int h;
return (value == null) ? 0 : Math.abs(seed * (cap - 1) & ((h = value.hashCode()) ^ (h >>> 16)));
}
}
}
測試:
package top.zysite.bloom.filter;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.junit.Test;
/**
* 布隆過濾器測試類
*
* @author Leo
* @create 2020/12/25 15:15
**/
public class MyBloomFilterTest {
/**
* 測試自己實現的布隆過濾器
*/
@Test
public void testMyBloomFilter() {
//建立布隆過濾器物件
MyBloomFilter myBloomFilter = new MyBloomFilter();
String str1 = "MyBloomFilterTest";
String str2 = "testMyBloomFilter";
System.out.println(myBloomFilter.contains(str1));
System.out.println(myBloomFilter.contains(str2));
//將元素放入布隆過濾器
myBloomFilter.add(str1);
myBloomFilter.add(str2);
System.out.println(myBloomFilter.contains(str1));
System.out.println(myBloomFilter.contains(str2));
}
/**
* 測試 Guava 提供的布隆過濾器
*/
@Test
public void testGuavaBloomFilter(){
// 建立布隆過濾器物件
BloomFilter<Integer> filter = BloomFilter.create(
Funnels.integerFunnel(),
1500,
0.01);
// 判斷指定元素是否存在
System.out.println(filter.mightContain(1));
System.out.println(filter.mightContain(2));
// 將元素新增進布隆過濾器
filter.put(1);
filter.put(2);
System.out.println(filter.mightContain(1));
System.out.println(filter.mightContain(2));
}
}
使用 Guava 提供的實現需引入以下依賴:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
Redis 中使用布隆過濾器
Redis v4.0 之後有了 Module(模組/外掛) 功能,Redis Modules 讓 Redis 可以使用外部模組擴充套件其功能 。布隆過濾器就是其中的 Module。
官網推薦了一個 RedisBloom 作為 Redis 布隆過濾器的 Module 地址:https://github.com/RedisBloom/RedisBloom
根據專案的 Readme 可以快速入門。
我的更多文章盡在:我的個人部落格