GUAVA之布隆过滤器

布隆过滤器

布隆过滤器 (Bloom Filter)是由Burton Howard Bloom于1970年提出,它是一种space efficient的几率型数据结构,用于判断一个元素是否在集合中。在垃圾邮件过滤的黑白名单方法、爬虫(Crawler)的网址判重模块中等等常常被用到。哈希表也能用于判断元素是否在集合中,可是布隆过滤器只须要哈希表的1/8或1/4的空间复杂度就能完成一样的问题。布隆过滤器能够插入元素,但不能够删除已有元素。其中的元素越多,false positive rate(误报率)越大,可是false negative (漏报)是不可能的。java

算法描述

一个empty bloom filter是一个有m bits的bit array,每个bit位都初始化为0。而且定义有k个不一样的hash function,每一个都以uniform random distribution将元素hash到m个不一样位置中的一个。在下面的介绍中n为元素数,m为布隆过滤器或哈希表的slot数,k为布隆过滤器重hash function数。web

为了add一个元素,用k个hash function将它hash获得bloom filter中k个bit位,将这k个bit位置1。算法

为了query一个元素,即判断它是否在集合中,用k个hash function将它hash获得k个bit位。若这k bits全为1,则此元素在集合中;若其中任一位不为1,则此元素比不在集合中(由于若是在,则在add时已经把对应的k个bits位置为1)。数据结构

不容许remove元素,由于那样的话会把相应的k个bits位置为0,而其中颇有可能有其余元素对应的位。所以remove会引入false negative,这是绝对不被容许的。dom

当k很大时,设计k个独立的hash function是不现实而且困难的。对于一个输出范围很大的hash function(例如MD5产生的128 bits数),若是不一样bit位的相关性很小,则可把此输出分割为k份。或者可将k个不一样的初始值(例如0,1,2, … ,k-1)结合元素,feed给一个hash function从而产生k个不一样的数。ide

当add的元素过多时,即n/m过大时(n是元素数,m是bloom filter的bits数),会致使false positive太高,此时就须要从新组建filter,但这种状况相对少见。svg

简单的布隆过滤器实现

public   class  SimpleBloomFilter {  

     private   static   final   int    DEFAULT_SIZE  =   2   <<   24 ;  
     private   static   final   int [] seeds         =   new   int [] {  7 ,  11 ,  13 ,  31 ,  37 ,  61 , };  
     private  BitSet             bits          =   new  BitSet(DEFAULT_SIZE);  
     private  SimpleHash[]       func          =   new  SimpleHash[seeds.length];  
     public   static   void  main(String[] args) {  
        String value  =   " stone2083@yahoo.cn " ;  
        SimpleBloomFilter filter  =   new  SimpleBloomFilter();  
        System.out.println(filter.contains(value));  
        filter.add(value);  
        System.out.println(filter.contains(value));  
    }  
     public  SimpleBloomFilter() {  
         for  ( int  i  =   0 ; i  <  seeds.length; i ++ ) {  
            func[i]  =   new  SimpleHash(DEFAULT_SIZE, seeds[i]);  
        }  
    }  
     public   void  add(String value) {  
         for  (SimpleHash f : func) {  
            bits.set(f.hash(value),  true );  
        }  
    }  
     public   boolean  contains(String value) {  
         if  (value  ==   null ) {  
             return   false ;  
        }  
         boolean  ret  =   true ;  
         for  (SimpleHash f : func) {  
            ret  =  ret  &&  bits.get(f.hash(value));  
        }  
         return  ret;  
    }  
     public   static   class  SimpleHash {  
         private   int  cap;  
         private   int  seed;  
         public  SimpleHash( int  cap,  int  seed) {  
             this .cap  =  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  (cap  -   1 )  &  result;  
        }  
    }  
}

GUAVA之布隆过滤器

import java.nio.charset.Charset;

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.PrimitiveSink;

public class BloomTest {
    // 预计元素个数
    private long size = 1024 * 1024;

    private BloomFilter<String> bloom = BloomFilter.create(new Funnel<String>() {
        @Override
        public void funnel(String from, PrimitiveSink into) {
            // 自定义过滤条件 此处不作任何过滤
            into.putString((CharSequence) from, Charset.forName("UTF-8"));
        }
    }, size);

    public void fun() {
        // 往过滤器中添加元素
        bloom.put("布隆过滤器");
        // 查询
        boolean mightContain = bloom.mightContain("布隆过滤器");
        System.out.println(mightContain);
    }

    public static void main(String[] args) {
        BloomTest blBoolmTest = new BloomTest();
        blBoolmTest.fun();
    }
}