• 基于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 }
  • 相关阅读:
    c#中判断对象为空的几种方式(字符串等)
    log4net示例3控制台、windows事件
    c#中如何截取Windows消息来触发自定义事件
    向ArcGIS的ToolBarControl中添加任意的windows组建的方法
    log4net示例1最简单的回滚文件记录日志程序(时间)
    Qt 定时器实现循环
    把 MPP Sample 编译成动态库
    Linux Shell 常用编程语法
    VSCode 调试
    Hisi 使用GDB调试(直接调试)
  • 原文地址:https://www.cnblogs.com/xiaoyh/p/10389963.html
Copyright © 2020-2023  润新知