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

来源:互联网 发布:武汉java 开发前景 编辑:程序博客网 时间:2024/05/02 04:39

最近写爬虫需要降低内存的占用,现在用的是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 误报率表:

这里写图片描述

最后是代码实现:

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;        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

运行结果:

这里写图片描述


0 0