BitMap再再体验之布隆过滤器

前言

还是从一个问题出发,如果需要判断一个元素是否存在应该用什么数据结构?

比较常用的是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"));

    }
}
posted on 2020-09-09 17:24  斜月三星一太阳  阅读(304)  评论(0编辑  收藏  举报