摘要

BloomFilter提供了一种判断一个值是否存在于指定集合的方法。在一个数据量较低的场景下,将数据保存到Hashset,然后利用hash检索某个值是否存在,但是在一个非常大的数据集中,传统的方法会消耗大量的内存,这导致OOM问题。BloomFilter的思路是,将一个value经过多次不同的hash映射到一个有限长度的bit数组中。当检测某个值是否存在的时候,只需要使用相同的hash检测,在bit数组中同时存在,则证明value大概率出现在集合中。如果有任何一次hash的index在bit map中被标记为0(代表无效),则证明value 一定不出现在该集合中。

之所以出现这个原因(大概率出现),是因为A和B两个value经过多次hash后,可能同时将bit map中相同的index标记为1。 使用bloomFilter可以节省大量的内存,在很多对结果要求并不很严格的场景下,bloomFilter是高效的。


核心实现的代码如下


public class BloomFilter {

    private static final int MAX_HASH_COUNT=3;
    private byte[] map;
    private int size;

    public BloomFilter(int size){
        this.size=size;
        this.map=new byte[size];
        for (int i=0;i<map.length;i++){
            map[i]=0;
        }
    }

    public void add(String e){
        for (int i=1;i<=MAX_HASH_COUNT;i++) {
            int index=hashCodeGroup(e,i);
            map[index]=(byte)1;
        }
    }

    public boolean isExists(String e){

        int checkPoint=0;
        for (int i=1;i<=MAX_HASH_COUNT;i++) {
            if(map[hashCodeGroup(e,i)]==1){
                checkPoint++;
            }
        }
        return checkPoint==MAX_HASH_COUNT;
    }

    private int hashCodeGroup(String e,int loopIndex)
    {
        switch (loopIndex){
            case 1:
                return hashCode1(e)% this.size;
            case 2:
                return hashCode2(e)% this.size;
            case 3:
                return hashCode3(e)% this.size;
            default:
                return 0;
        }
    }

    private int hashCode1(String data){

        int hash = 0;
        int i;
        for (i = 0; i < data.length(); ++i) {
            hash = 33 * hash + data.charAt(i);
        }
        return Math.abs(hash);
    }

    private int hashCode2(String data){
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < data.length(); i++) {
            hash = (hash ^ data.charAt(i)) * p;
        }
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        return Math.abs(hash);

    }
    private int hashCode3(String key) {
        int hash, i;
        for (hash = 0, i = 0; i < key.length(); ++i) {
            hash += key.charAt(i);
            hash += (hash << 10);
            hash ^= (hash >> 6);
        }
        hash += (hash << 3);
        hash ^= (hash >> 11);
        hash += (hash << 15);
        return Math.abs(hash);
    }


}

BloomFilter 的准确率取决于size和hash次数,以及好的Hash函数选择。 在防止缓存击穿这类问题上,可以使用Bloomfilter。