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

    xiaoxiao2021-12-14  58

    最近写爬虫需要降低内存的占用,现在用的是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; } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384

    运行结果:

    转载请注明原文地址: https://ju.6miu.com/read-964908.html

    最新回复(0)