基于Java实现简化版本的布隆过滤器

一、布隆过滤器:

  布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。布隆过滤器是与哈希算法是相关的,是工业实践上常用的算法,之前我们使用HashMap或者HashSet来查找重复的话也是可以的,但是对于在数据量比较大的情况下去查询那么速度就比较慢了,这个时候对于大的数据量来进行检索使用布隆过滤查找速度就比较快。

  如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树、散列表(又叫哈希表,HashTable)等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,同时检索速度也越来越慢,上述三种结果的检索时间分别为O(n),O(logn),O(n/k)。

  布隆过滤器的原理是,当一个元素被加入集合时,通过k个散列函数将这个元素映射成一个位数组(位阵列 Bit array)中的k个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在,如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。

    

  下面使用md5算法计算出哈希值并在对应的位图上置1,我们也可以使用其他的哈希函数计算出哈希值比如直接取余法,乘法这些,使用任何的哈希算法计算出来的哈希值对于检索的结果是没有影响的,只是检索的效率会有所不同。并且这里使用到了BigInteger类用来处理生成的比较大的数字。

二、代码:

 1 import java.math.BigInteger;
 2 import java.security.MessageDigest;
 3 import java.security.NoSuchAlgorithmException;
 4 import java.util.ArrayList;
 5 import java.util.logging.Level;
 6 import java.util.logging.Logger;
 7 
 8 // 扩展知识  不要求掌握
 9 /** 简化版本的布隆过滤器的实现 */
10 public class BloomFilter {
11     public static final int NUM_SLOTS = 1024 * 1024 * 8;// 位图的长度
12     public static final int NUM_HASH = 8;// hash函数的个数,一个hash函数的结果用于标记一个位
13     private BigInteger bitmap = new BigInteger("0");// 位图
14 
15     public static void main(String[] args) {
16         // 测试代码
17         BloomFilter bf = new BloomFilter();
18         ArrayList<String> contents = new ArrayList<>();
19         contents.add("sldkjelsjf");
20         contents.add("ggl;ker;gekr");
21         contents.add("wieoneomfwe");
22         contents.add("sldkjelsvrnlkjf");
23         contents.add("ksldkflefwefwefe");
24 
25         for (int i = 0; i < contents.size(); i++) {
26             bf.addElement(contents.get(i));
27         }
28         System.out.println(bf.check("sldkjelsvrnlkjf"));   // true
29         System.out.println(bf.check("sldkjelnlkjf"));    // false
30         System.out.println(bf.check("ggl;ker;gekr"));   // true
31     }
32 
33     /** 将message+n映射到0~NUM_SLOTS-1之间的一个值 */
34     private int hash(String message, int n) {
35         message = message + String.valueOf(n);
36         try {
37             MessageDigest md5 = MessageDigest.getInstance("md5");// 将任意输入映射成128位(16个字节)整数的hash函数
38             byte[] bytes = message.getBytes();
39             md5.update(bytes);
40             byte[] digest = md5.digest();
41             BigInteger bi = new BigInteger(digest);// 至此,获得message+n的md5结果(128位整数)
42 
43             return Math.abs(bi.intValue()) % NUM_SLOTS;
44         } catch (NoSuchAlgorithmException ex) {
45             Logger.getLogger(BloomFilter.class.getName()).log(Level.SEVERE, null, ex);
46         }
47         return -1;
48         // return (int)Math.abs(HashFunctions.bernstein(message,NUM_SLOTS));
49     }
50 
51     /*
52      * 处理原始数据 1.hash1(msg)标注一个位…… hash的值域0~NUM_SLOTS-1
53      */
54     public void addElement(String message) {
55         for (int i = 0; i < NUM_HASH; i++) {
56             int hashcode = hash(message, i);// 代表了hash1,hash2……hash8
57             // 结果,用于标注位图的该位为1
58             if (!bitmap.testBit(hashcode)) {// 如果还不为1
59                 // 标注位图的该位为1
60                 bitmap = bitmap.or(new BigInteger("1").shiftLeft(hashcode));
61             }
62         }
63 
64     }
65 
66     public boolean check(String message) {
67         for (int i = 0; i < NUM_HASH; i++) {
68             int hashcode = hash(message, i);
69             // hashcode代表一个位置
70             if (!this.bitmap.testBit(hashcode)) {
71                 // 如果位图的该位为0,那么message一定不存在
72                 return false;
73             }
74         }
75         return true;// 不精确,有可能误判
76     }
77 }

 

posted @ 2019-02-17 00:39  |旧市拾荒|  阅读(597)  评论(0编辑  收藏  举报