BloomFilter布隆过滤器

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个函数去存这些数字(put时,会使用每个hash函数对值进行hash,然后分别存到大的bit数组中,mightContain的时候,也会通过该方式取出来,只要发现某一位上没有,则为false)。

 

使用第三个create方法,我们设置下错误率:

private static BloomFilter<Integer> bf = BloomFilter.create(Funnels.integerFunnel(), total, 0.0003);

再运行看看:

此时误伤的数量为4,错误率为0.04%左右。

当错误率设为0.0003时,所需要的位数为16883499,1600万位,需要12个函数

和上面对比可以看出,错误率越大,所需空间和时间越小,错误率越小,所需空间和时间约大

 

posted @ 2018-09-10 17:38  仅此而已-远方  阅读(2616)  评论(0编辑  收藏  举报