基于java实现自定义布隆过滤器一

2 篇文章 0 订阅

布隆过滤器是什么

布隆过滤器底层基于bitmap,根据hash函数结果维护。

优点

  1. 布隆过滤器解决大数据量的”是否存在“问题很好用,能节省大量空间。

缺点

  1. 布隆过滤器存在一定的误判率,只能确认数据一定不存在,无法确认数据是否一定存在。对于要求精确的数据不适用。
  2. 布隆过滤器不支持扩展。随着数据量的上升,误判率会跟着上升。此时如果要扩展布隆过滤器,只能新建一个新的布隆过滤器。

基于redis实现布隆过滤器

redis提供setbit等命令,允许对bitmap进行位级别的操作。借助redis提供的接口,不需要自己去实现布隆过滤器,但在需要频繁和布隆过滤器交互的场景不适合使用。

比如在匹配算法中,要求匹配过的人不能重复匹配到一起。在一个时间复杂度为n^2的匹配过程中,如果每次判断都去访问redis的布隆过滤器,可能会导致千万级别的网络交互,大大降低性能。因此实现一套自定义的布隆过滤器显得尤其重要。只需要在匹配开始前,从redis获取布隆过滤器到jvm内存,匹配过程一边判断一边更新布隆过滤器,结束的时候将布隆过滤器更新到redis即可。这样,一次匹配过程只需要和redis交互两次。

自定义布隆过滤器

public class CustomizeBloom {

    //预计插入量
    private long expectedInsertions = 1000000;

    //可接受的错误率
    private double fpp = 0.01;

    //bit数组长度
    private int numBits;
    
    //hash函数数量
    private int numHashFunctions;

    private long[] bitMap;

    private int bitMapLength;

    public static void main(String[] args) {
        CustomizeBloom customizeBloomJob = new CustomizeBloom();
        customizeBloomJob.initParams();
        int exitsNums = 0;
        for (int i = 0; i < 800000; i++) {
            customizeBloomJob.put(String.valueOf(i));
        }

        for (int i = 0; i <1000000; i++) {
            if (customizeBloomJob.isExist(String.valueOf(i))) {
                exitsNums++;
            }
        }

        System.out.println("exitsNums = " + exitsNums);
    }
    
    public void initParams() {
        this.numBits = (int)optimalNumOfBits(expectedInsertions, fpp);
        this.numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);
        initBitMap();
    }


    private void initBitMap() {
        long bitMapLength = this.numBits / 64;
        if ((this.numBits & 63) != 0) {
            bitMapLength++;
        }
        if (bitMapLength > Integer.MAX_VALUE) {
            throw new RuntimeException("数组长度太长");
        }

        this.bitMap = new long[(int)bitMapLength];
        this.bitMapLength = (int)bitMapLength;
    }

    //计算hash函数个数
    private int optimalNumOfHashFunctions(long n, long m) {
        return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
    }

    //计算bit数组长度
    private long optimalNumOfBits(long n, double p) {
        if (p == 0) {
            p = Double.MIN_VALUE;
        }
        return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
    }

    /**
     * 判断keys是否存在于集合
     */
    public boolean isExist(String key) {
        int[] indexs = getIndexs(key);
        for (int index : indexs) {
            int bitMapIndex = index / 64;
            int bitIndex = index & 63;
            long l = (long)1 << bitIndex;
            long result = this.bitMap[bitMapIndex] & l;
            if (result == 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * 将key存入redis bitmap
     */
    public void put(String key) {
        int[] indexs = getIndexs(key);
        for (int index : indexs) {
            int bitMapIndex = index / 64;
            int bitIndex = index & 63;
            long l = this.bitMap[bitMapIndex];
            long i = (long)1 << bitIndex;
            long result = l | i;
            this.bitMap[bitMapIndex] = result;
        }
    }

    /**
     * 根据key获取bitmap下标数组
     */
    private int[] getIndexs(String key) {
        int[] indexs = new int[numHashFunctions];
        // 不允许为负数
        for (int i = 0; i < numHashFunctions; i++) {
            int index = hash1(key) + i*hash2(key);
            if (index < 0) {
                index = ~index;
            }
            indexs[i] = index % numBits;
        }
        return indexs;
    }

    public int hash1(String key) {
        int h;
        return (h = key.hashCode()) ^ (h >>> 16);
    }

    public int hash2(String key) {
        int h;
        return h = key.hashCode();
    }
}

优化方向

  1. 封装成一个模块,适用于任何需要判断”是否存在“的功能
  2. 设置bitmap的长度为2^n次方,方便做取余运算
  3. 根据预插入量和容错率,支持动态扩展。如果bitmap过大,无法依靠一维数组维护,可扩展为二维数组维护
  4. 增加对边界值的判断,增强代码的健壮性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值