前言
还是从一个问题出发,如果需要判断一个元素是否存在应该用什么数据结构?
比较常用的是HashMap, 我们回到BitMap初体验中的那个问题,给你一台 4G 内存的机器,一组 20 亿个元素(这个元素有可能是字符串,也有可能是一个对象),你怎么判断一个元素是否存在?这个时候我们就需要用到一种数据结构-布隆过滤器。
实际中的业务场景:redis 的缓存穿透
什么是布隆过滤器
布隆过滤器本质上是一种比较巧妙的概率型数据结构,它可以告诉你某个元素一定不存在或者有可能存在。
优势和缺点
优势:
相比于HashMap, 它更高效,占用的空间更小
缺点:
它的返回结果是概率性的,不是确切的
实现原理
当插入一个元素时,将该元素分别输入k个哈希函数,产生k个哈希值,将这些哈希值存到BitMap中。
当需要查询一个元素时,同样将该元素分别输入k个哈希函数,得到k个哈希值,然后判断这k个哈希值是否都存在。如果有一个哈希值不存在,则说明这个元素一定不存在;如果这k个哈希值都存在,则说明这个元素可能存在(有可能哈希冲突)。
代码
首先定义hash函数
interface HashFunction<T> {
int hash(T t);
}
class DefaultHashFunction<T> implements HashFunction<T> {
@Override
public int hash(T t) {
return t == null ? 0 : t.hashCode();
}
}
class StringHashFunction implements HashFunction<String> {
@Override
public int hash(String str) {
if (str == null) {
return 0;
}
int hash = 0;
char[] chars = str.toCharArray();
for (int i = 0; i < chars.length; i++) {
hash = chars[i] + (hash << 6) + (hash << 16) - hash;
}
return hash;
}
}
构造布隆过滤器
class BloomFilter {
private BitSet bitSet;
private int DEFAULT_SIZE = 1 << 30;
private List<HashFunction<String>> hfs;
public BloomFilter() {
bitSet = new BitSet(DEFAULT_SIZE);
hfs = new ArrayList<HashFunction<String>>() {
{
add(new DefaultHashFunction<>());
add(new StringHashFunction());
}
};
}
public void add(String value) {
if (value != null) {
for (HashFunction<String> hf : hfs) {
int hash = hf.hash(value);
hash = hash < 0 ? Math.abs(hash) : hash;
bitSet.set(hash);
}
}
}
public boolean contains(String value) {
if (value == null) {
return false;
}
for (HashFunction hf : hfs) {
int hash = hf.hash(value);
hash = hash < 0 ? Math.abs(hash) : hash;
if (!bitSet.get(hash)) {
return false;
}
}
return true;
}
}
main 方法
public class Demo {
public static void main(String[] args) {
BloomFilter bf = new BloomFilter();
bf.add("boy");
bf.add("girl");
bf.add("mike");
bf.add("jane");
System.out.println(bf.contains("robot"));
System.out.println(bf.contains("boy"));
}
}