布隆过滤器(Bloom Filter)之java实例

 在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断它是否在已知的字典中);在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上;在网络爬虫里,一个网址是否被访问过等等。最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新元素时,将它和集合中的元素直接比较即可。一般来讲,计算机中的集合是用哈希表(hash table)来存储的。它的好处是快速准确,缺点是费存储空间。当集合比较小时,这个问题不显著,但是当集合巨大时,哈希表存储效率低的问题就显现出来了。比如说,一个象 Yahoo,Hotmail 和 Gmai 那样的公众电子邮件(email)提供商,总是需要过滤来自发送垃圾邮件的人(spamer)的垃圾邮件。一个办法就是记录下那些发垃圾邮件的 email 地址。由于那些发送者不停地在注册新的地址,全世界少说也有几十亿个发垃圾邮件的地址,将他们都存起来则需要大量的网络服务器。如果用哈希表,每存储一亿个 email 地址, 就需要 1.6GB 的内存(用哈希表实现的具体办法是将每一个 email 地址对应成一个八字节的信息指纹 googlechinablog.com/2006/08/blog-post.html,然后将这些信息指纹存入哈希表,由于哈希表的存储效率一般只有 50%,因此一个 email 地址需要占用十六个字节。一亿个地址大约要 1.6GB, 即十六亿字节的内存)。因此存贮几十亿个邮件地址可能需要上百 GB 的内存。除非是超级计算机,一般服务器是无法存储的。

今天,我们介绍一种称作布隆过滤器的数学工具,它只需要哈希表 1/8 到 1/4 的大小就能解决同样的问题。

布隆过滤器是由巴顿.布隆于一九七零年提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。我们通过上面的例子来说明起工作原理。

假定我们存储一亿个电子邮件地址,我们先建立一个十六亿二进制(比特),即两亿字节的向量,然后将这十六亿个二进制全部设置为零。对于每一个电子邮件地址 X,我们用八个不同的随机数产生器(F1,F2, ...,F8) 产生八个信息指纹(f1, f2, ..., f8)。再用一个随机数产生器 G 把这八个信息指纹映射到 1 到十六亿中的八个自然数 g1, g2, ...,g8。现在我们把这八个位置的二进制全部设置为一。当我们对这一亿个 email 地址都进行这样的处理后。一个针对这些 email 地址的布隆过滤器就建成了。(见下图)



现在,让我们看看如何用布隆过滤器来检测一个可疑的电子邮件地址 Y 是否在黑名单中。我们用相同的八个随机数产生器(F1, F2, ..., F8)对这个地址产生八个信息指纹 s1,s2,...,s8,然后将这八个指纹对应到布隆过滤器的八个二进制位,分别是 t1,t2,...,t8。如果 Y 在黑名单中,显然,t1,t2,..,t8 对应的八个二进制一定是一。这样在遇到任何在黑名单中的电子邮件地址,我们都能准确地发现。

布隆过滤器决不会漏掉任何一个在黑名单中的可疑地址。但是,它有一条不足之处。也就是它有极小的可能将一个不在黑名单中的电子邮件地址判定为在黑名单中,因为有可能某个好的邮件地址正巧对应个八个都被设置成一的二进制位。好在这种可能性很小。我们把它称为误识概率。在上面的例子中,误识概率在万分之一以下。

布隆过滤器的好处在于快速,省空间。但是有一定的误识别率。常见的补救办法是在建立一个小的白名单,存储那些可能别误判的邮件地址。

package com.huigao.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.BitSet;    

public class BloomFilter {    
	 private int defaultSize = 5000 << 10000;   
	    private int basic = defaultSize -1;   
	    private String key = null;   
	    private BitSet bits = new BitSet(defaultSize);   
	    
	    public BitSet getBits() {
			return bits;
		}

		public void setBits(BitSet bits) {
			this.bits = bits;
		}

		public BloomFilter(){
	    	
	    }
	       
	    public BloomFilter(String key){   
	        this.key = key;   
	    }   
	       
	    private int[] lrandom(String key){   
	        int[] randomsum = new int[8];   
	        int random1 = hashCode(key,1);   
	        int random2 = hashCode(key,2);   
	        int random3 = hashCode(key,3);   
	        int random4 = hashCode(key,4);   
	        int random5 = hashCode(key,5);   
	        int random6 = hashCode(key,6);   
	        int random7 = hashCode(key,7);   
	        int random8 = hashCode(key,8);   
	        randomsum[0] = random1;   
	        randomsum[1] = random2;   
	        randomsum[2] = random3;   
	        randomsum[3] = random4;   
	        randomsum[4] = random5;   
	        randomsum[5] = random6;   
	        randomsum[6] = random7;   
	        randomsum[7] = random8;   
	        return randomsum;   
	    }   
	       
	 /*   private int[] sameLrandom(){   
	        int[] randomsum = new int[8];   
	        int random1 = hashCode(key,1);   
	        int random2 = hashCode(key,1);   
	        int random3 = hashCode(key,1);   
	        int random4 = hashCode(key,1);   
	        int random5 = hashCode(key,1);   
	        int random6 = hashCode(key,1);   
	        int random7 = hashCode(key,1);   
	        int random8 = hashCode(key,1);   
	        randomsum[0] = random1;   
	        randomsum[1] = random2;   
	        randomsum[2] = random3;   
	        randomsum[3] = random4;   
	        randomsum[4] = random5;   
	        randomsum[5] = random6;   
	        randomsum[6] = random7;   
	        randomsum[7] = random8;   
	        return randomsum;   
	    }   */
	       
	    private void add(String key){   
	        if(exist( key)){   
	            System.out.println("已经包含("+key+")");   
	            return;   
	        }   
	        int keyCode[] = lrandom(key);   
	        bits.set(keyCode[0]);   
	        bits.set(keyCode[1]);   
	        bits.set(keyCode[2]);    
	        bits.set(keyCode[3]);    
	        bits.set(keyCode[4]);    
	        bits.set(keyCode[5]);    
	        bits.set(keyCode[6]);    
	        bits.set(keyCode[7]);   
	    }   
	       
	    private boolean exist(String key){   
	        int keyCode[] = lrandom(key);   
	        if(bits.get(keyCode[0])&&   
	                bits.get(keyCode[1])   
	                &&bits.get(keyCode[2])   
	                &&bits.get(keyCode[3])   
	                &&bits.get(keyCode[4])   
	                &&bits.get(keyCode[5])   
	                &&bits.get(keyCode[6])   
	                &&bits.get(keyCode[7])){   
	            return true;    
	        }   
	        return false;   
	    }   
	       
//	    private boolean set0(){   
//	        if(exist()){   
//	            int keyCode[] = lrandom();   
//	            bits.clear(keyCode[0]);   
//	            bits.clear(keyCode[1]);   
//	            bits.clear(keyCode[2]);   
//	            bits.clear(keyCode[3]);   
//	            bits.clear(keyCode[4]);   
//	            bits.clear(keyCode[5]);   
//	            bits.clear(keyCode[6]);   
//	            bits.clear(keyCode[7]);   
//	            return true;   
//	        }   
//	        return false;   
//	    }   
	       
	    private int hashCode(String key,int Q){   
	        int h = 0;   
	        int off = 0;   
	        char val[] = key.toCharArray();   
	        int len = key.length();   
	        for (int i = 0; i < len; i++) {   
	            h = (30 + Q) * h + val[off++];   
	        }   
	        return changeInteger(h);   
	    }   
	       
	    private int changeInteger(int h) {   
	        return basic & h;   
	    }   
	       
	    public void saveBit(String filename){
	    	
	    	try {
	    		File file=new File(filename);
				ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(file,false));
				oos.writeObject(bits);
				oos.flush();
				oos.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
	    }
	    
	    public BitSet readBit(String filename){
	    	BitSet bits=new BitSet(defaultSize);
	       File file=new File(filename);
	       if(!file.exists()){
	    	   return bits;
	       }
	       try {
			ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
			 bits=(BitSet)ois.readObject();
			ois.close();
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return bits;
	    }
	    public static void main(String[] args) {   
	    	
	    	String fileName="c:\\test\\BloomFilter.txt";
	    	String url="http://www.agrssdddd.com/";
	    	BloomFilter bf=new BloomFilter();
	    	BitSet bitSet=bf.readBit(fileName);
	    	bf.setBits(bitSet);
	    	bf.add(url);
	    System.out.println(bf.exist(url));
	    bf.saveBit(fileName);
	    
	    	
	      /*  BloomFilter f = new BloomFilter("http://www.agrilink.cn/");   
	        f.add();   
	        System.out.println(f.exist());   */
//	        f.set0();   
//	        System.out.println(f.exist());   
	    }   

}   

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
布隆过滤器Bloom Filter)是一种重要的数据结构,它用于快速判断一个元素是否存在于一个集合中。布隆过滤器的核心思想是通过一系列哈希函数来对元素进行多次哈希,然后将得到的哈希值映射到一个位数组中,并将对应的位置设为1。当需要判断一个元素是否存在时,同样对其进行多次哈希,检查对应位数组的值是否都为1,若都为1则可以确定元素可能存在;若存在一个0,则可以确定元素一定不存在。因此,布隆过滤器是一种基于概率的数据结构,可以高效地进行查找。 然而,布隆过滤器也存在一些问题。首先,由于多个不同的元素可能会哈希到相同的位上,因此在查询时可能出现误判,即判断一个元素存在时实际上并不存在。这种误判是由于多个元素共享了某一位的原因导致的。其次,布隆过滤器的特性决定了它无法支持元素的删除操作,因为删除一个元素可能会影响其他元素的判断结果,从而增加误判率。 要注意的是,计数布隆过滤器(Counting Bloom Filter)提供了一种实现删除操作的可能性,但并不能保证在后续查询时该值一定返回不存在。因此,不能说计数布隆过滤器支持删除,而是说计数布隆过滤器提供了实现删除的可能。 [3<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【海量数据处理】布隆过滤器BloomFilter](https://blog.csdn.net/qq_43727529/article/details/127180864)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [Java --- redis7之布隆过滤器BloomFilter](https://blog.csdn.net/qq_46093575/article/details/130613434)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值