Java实现布隆过滤器(已爬URL过滤)

最近写爬虫需要降低内存的占用,现在用的是HashSet进行已爬URL的过滤,所以想到用布隆过滤器(Bloom Filter)来替换,从而减少内存的开销。因为HashSet内部是由HashMap处理的,HashMap则通过计算一个int型的hash值得出信息指纹,所以一个信息指纹占4字节,但是由于哈希的存储效率一般只有一半,所有说一条URL就需要8字节的信息指纹,而Bloom Filter 则只需要其1/2或更小的信息指纹就可以。

说一下Bloom Filter的工作原理:

假设有一条URL,那么就先建立32个二进制常量(这里个数还可以取其他值,但是误报率会不同,下面会有一张表格显示)。即4字节的向量,然后将这32个二进制位全部设置为0,对于这条URL,用8个不同的随机数产生8个信息指纹,再用一个随机数产生器把这8个信息指纹映射到1到32的8个自然数,并把这些位置置为1。

如果要检测某条URL是否在这个Bloom Filter中,我们仍然用上述8个随机数产生8个信息指纹,并将这8个指纹对应到布隆过滤器的8个二进制位,如果8位都为1,则说明这条URL在这个Bloom Filter中,否则只要有一位不为1,就说明不在。

Bloom Filter绝不会漏掉任何一个重复的URL,但可能会有误报情况,虽然这种可能性很小,上述说的误报概率只有千万分之一,可以通过建立一个小的名单,存储可能误判的URL,并进行比较。

Bloom Filter 误报率表:

Java实现布隆过滤器(已爬URL过滤)_第1张图片

最后是代码实现:

public class Test {
    public static void main(String[] args){

        BloomFilter b = new BloomFilter();
        b.addValue("www.baidu.com");
        b.addValue("www.sohu.com");

        System.out.println(b.contains("www.baidu.com"));
        System.out.println(b.contains("www.sina.com"));
    }
}

class BloomFilter{

    private static final int BIT_SIZE = 2 << 28 ;//二进制向量的位数,相当于能存储1000万条url左右,误报率为千万分之一
    private static final int[] seeds = new int[]{3, 5, 7, 11, 13, 31, 37, 61};//用于生成信息指纹的8个随机数,最好选取质数

    private BitSet bits = new BitSet(BIT_SIZE);
    private Hash[] func = new Hash[seeds.length];//用于存储8个随机哈希值对象

    public BloomFilter(){
        for(int i = 0; i < seeds.length; i++){
            func[i] = new Hash(BIT_SIZE, seeds[i]);
        }
    }

    /**
     * 像过滤器中添加字符串
     */
    public void addValue(String value)  
    {  
        //将字符串value哈希为8个或多个整数,然后在这些整数的bit上变为1
        if(value != null){
            for(Hash f : func) 
                bits.set(f.hash(value), true); 
        }

    }  

    /**
     * 判断字符串是否包含在布隆过滤器中
     */
    public boolean contains(String value)  
    {  
        if(value == null) 
            return false;  

        boolean ret = true;  

        //将要比较的字符串重新以上述方法计算hash值,再与布隆过滤器比对
        for(Hash f : func)
            ret = ret && bits.get(f.hash(value));  
        return ret;  
    }  

    /**
     * 随机哈希值对象
     */

    public static class Hash{
        private int size;//二进制向量数组大小
        private int seed;//随机数种子

        public Hash(int cap, int seed){
            this.size = cap;
            this.seed = seed;
        }

        /**
         * 计算哈希值(也可以选用别的恰当的哈希函数)
         */
        public int hash(String value){
            int result = 0;
            int len = value.length();
            for(int i = 0; i < len; i++){
                result = seed * result + value.charAt(i);
            }

            return (size - 1) & result;
        }
    }

}

运行结果:

这里写图片描述

你可能感兴趣的:(Java)