布隆(bloom)过滤器学习及简单Java实现

对于广大程序员来说,“判断一个值是否存在”这么一个问题,可能新手会说遍历(数组),老手们会直接抛下一句“哈希表呗”然后不屑的离开。

但是经过我们源码的分析可知,HashMap的判断,摊还分析确实是O(1)搜索时间,但是实际上就找到对应节点的过程(可以看我前面的博客)还是需要一定的时间的。假设我们构造好了链表之后,某一个位置的链表长度为7(嘿嘿,如果是8就是红黑树了,需要3次搜索),那么我们每一次判断都需要一个7次搜索,次数过多必然效率有所下降。

 

那么,布隆过滤器是干什么的呢?

它可以给你一个对于这个值存在还是不存在的一个不准确回答:

可能存在。。。 /     一定不存在!

 

首先它是一个类似于位图一样的东西:(图片来源于知乎)

这个位代表一个byte,这个时候我们需要把一个key映射成几个hash值(你也可以选择只hash一次,但正确率肯定不高)。

比如单词“hello”三次hash(三个hash函数是不一样的)生成三个值为 (1,4,7),那么就变成:

 

然后单词“world”同理,生成的三个值是(3,4,8):

由于这里面hash只能使某一位“置位”,所以如果有个单词“Java”生成了(1,7,8),这里面我们可以看到确实这三个位置都处于置位状态,那么我们就可以说一定存在吗?答案是:不一定。

 

所以综上所述,如果生成的几个hash值都被置位那么可以说“可能存在”,否则我们就说“一定不存在”。

那其实,Bloom使用的正确姿势是什么呢?就是作为hash表(可能是HashMap也可能是ConcurrentHashMap,也可能是Redis之类的类NoSQL)的辅助结构,有了它能以一定的概率过滤不存在的搜索。

顺便贴出两个公式:

 

但其实,实际中的设计并不一定按照这个公式。

好了说了这么多,我写了一个Java版的简易过滤器,可以看看效果。

/**
 * @author wang66
 * 2019-8-12
 * Bloom for String
 */
public class Bloom {
    private int[] record=new int[1024];//每一个int代表32位
    private int size=32*1024;//位总长度
    private int hash1(String s){//第一个hash值
        int h=0;
        for(char c:s.toCharArray()){
            h=h*37+(int)(c);
        }
        return Math.abs(h);
    }
    private int hash2(String s){//第二个hash值
        int h=0;
        for(char c:s.toCharArray()){
            h=h*61+(int)c;
        }
        return Math.abs(h);
    }
    private int hash3(String s){//第三个hash值
        int h=0;
        char[] chars=s.toCharArray();
        for(int i=chars.length-1;i>=0;i--){
            h+=h*73+(int)chars[i];
        }
        return Math.abs(h);
    }
    public int hash4(String s){//第四个hash值
        int h=0;
        int j=1;
        char[] chars=s.toCharArray();
        for(char c:chars){
            h=h*41+(int)(c)*j;
            j*=-1;
        }
        return Math.abs(h);
    }
    public void put(String s){//把一个单词放进去
        int h1=hash1(s)%size;
        int h2=hash2(s)%size;
        int h3=hash3(s)%size;
        int h4=hash4(s)%size;
        record[h1/32]|=(1<<(h1%32));
        record[h2/32]|=(1<<(h2%32));
        record[h3/32]|=(1<<(h3%32));
        record[h4/32]|=(1<<(h4%32));
    }
    public boolean isMaybeExist(String s){//判断是否“可能存在”
        int h1=hash1(s)%size;
        int h2=hash2(s)%size;
        int h3=hash3(s)%size;
        int h4=hash4(s)%size;
        return (record[h1/32]&(1<<(h1%32)))!=0&&
                (record[h2/32]&(1<<(h2%32)))!=0&&
                (record[h3/32]&(1<<(h3%32)))!=0&&
                (record[h4/32]&(1<<(h4%32)))!=0;
    }
    public static void main(String[] args){
        Bloom bloom=new Bloom();
        bloom.put("Hello");
        bloom.put("world");
        bloom.put("friend");
        bloom.put("family");
        bloom.put("future");
        System.out.println(bloom.isMaybeExist("world"));
        System.out.println(bloom.isMaybeExist("poor"));
        System.out.println(bloom.isMaybeExist("future"));
    }
}

三个输出结果分别是

true
false
true

 

好了这个数据结构到此结束。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值