实现布隆过滤器

90 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

布隆过滤器简介

  • 布隆过滤器是一个很长的二进制向量和一系列随机映射函数。它可以用于检索一个元素是否在一个集合中。
  • 优点:空间效率和查询时间都远远超过一般的算法。
  • 缺点:有一定的误识别率和删除困难。

插入与查询

  • 布隆过滤器无论是在空间,还是时间方面都有着非常大的优势。因为它的存储空间和插入、查询时间都是 O(k)O(k) 级别的。同时为了方便由硬件并行实现,所设计的散列函数之间不能相关联。
  • 当元素数量比较少时,就可以使用散列表。但是随着存入的元素数量增加,误算率就会随之增加。而且我们不能够从布隆过滤器中删除元素。

src=http___img2020.cnblogs.com_blog_874518_202005_874518-20200507213409332-1282214032.png&refer=http___img2020.cnblogs.webp

参数与推导公式结果

  • 假定布隆过滤器长度(二进制位个数)为 m,散列函数个数 k ,误判率 p ,数据规模 n
  • 公式推到结果为:m=nlnp/(ln2)2m = -n * lnp / (ln2) ^ 2 , k=lnp/ln2k = -lnp / ln2

代码现实

public class BloomFilter<T> {
    /**
     * 二进制数向量长度
     * */
    private int bit_size;
    /**
     * 二进制数向量
     * */
    private long[] bits;
    /**
     * 散列函数个数
     * */
    private int hash_size;

    public BloomFilter(int n, double p) {
        if (n <= 0 || p <= 0 || p >= 1) {
            throw new IllegalArgumentException("param error!");
        }
        double ln2 = Math.log(2);
        double lnp = Math.log(p);
        bit_size = -(int)(n * lnp / ln2 * ln2);  // m
        hash_size = -(int)(lnp / ln2);           // k
        bits = new long[bit_size / Long.SIZE + (bit_size % Long.SIZE == 0 ? 0 : 1)];
    }

    public boolean put(T value) {
        if (value == null) return false;

        int hash1 = value.hashCode();
        int hash2 = hash1 >>> 16;

        boolean ans = false;
        for (int i = 1; i <= hash_size; i++) {
            int mix_hash = hash1 + (i * hash2);
            if (mix_hash < 0) {
                mix_hash = ~mix_hash;
            }

            int index = mix_hash % bit_size;
            if (set(index)) {
                ans = true;
            }
        }
        return ans;
    }

    public boolean contains(T value) {
        if (value == null) return false;

        int hash1 = value.hashCode();
        int hash2 = hash1 >>> 16;

        for (int i = 1; i <= hash_size; i++) {
            int mix_hash = hash1 + (i * hash2);
            if (mix_hash < 0) mix_hash = ~mix_hash;

            int index = mix_hash % bit_size;
            if (!get(index)) {
                return false;
            }
        }
        return true;
    }

    private boolean set(int index) {
        long value = bits[index / Long.SIZE];
        int bit_value = 1 << (index % Long.SIZE);
        bits[index / Long.SIZE] = value | bit_value;
        return (value & bit_value) == 0;
    }

    private boolean get(int index) {
        long value = bits[index / Long.SIZE];
        int bit_value = 1 << (index % Long.SIZE);
        return (value & bit_value) != 0;
    }
}

代码测试结果与分析

  • 由以下测试结果显示,误判率是比较接近我们设置的误判率 pp 的 。如果想要生产环境中使用的话,就可以考虑 Google 实现的布隆过滤器 点击这里 。当然在 redis 中也是有实现好的布隆过滤器。

屏幕截图 2022-04-04 161302.jpg