• 布隆过滤器简单实现添加和判断功能


    布隆过滤器

    布隆过滤器是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。主要用于判断一个元素是否在一个集合中。

    通常我们会遇到很多要判断一个元素是否在某个集合中的业务场景,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间也会呈现线性增长,最终达到瓶颈。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为O ( n ) O(n)O(n),O ( l o g n ) O(logn)O(logn),O ( 1 ) O(1)O(1)。

    这个时候,布隆过滤器(Bloom Filter)就应运而生。

    初始态

    添加两个值

    误判率

    布隆过滤器的误判是指多个输入经过哈希之后在相同的bit位置1了,这样就无法判断究竟是哪个输入产生的,因此误判的根源在于相同的 bit 位被多次映射且置 1。

    这种情况也造成了布隆过滤器的删除问题,因为布隆过滤器的每一个 bit 并不是独占的,很有可能多个元素共享了某一位。如果我们直接删除这一位的话,会影响其他的元素。(比如上图中的第 3 位)

    优点

    相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数 O ( K ) O(K)O(K),另外,散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

    布隆过滤器可以表示全集,其它任何数据结构都不能;

    缺点

    但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。

    另外,一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面。这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

    在降低误算率方面,有不少工作,使得出现了很多布隆过滤器的变种。

    在程序的世界中,布隆过滤器是程序员的一把利器,利用它可以快速地解决项目中一些比较棘手的问题。

    如网页 URL 去重、垃圾邮件识别、大集合中重复元素的判断和缓存穿透等问题。

    布隆过滤器的典型应用有:

    • 数据库防止穿库。 Google Bigtable,HBase 和 Cassandra 以及 Postgresql 使用BloomFilter来减少不存在的行或列的磁盘查找。避免代价高昂的磁盘查找会大大提高数据库查询操作的性能。
    • 业务场景中判断用户是否阅读过某视频或文章,比如抖音或头条,当然会导致一定的误判,但不会让用户看到重复的内容。
    • 缓存宕机、缓存击穿场景,一般判断用户是否在缓存中,如果在则直接返回结果,不在则查询db,如果来一波冷数据,会导致缓存大量击穿,造成雪崩效应,这时候可以用布隆过滤器当缓存的索引,只有在布隆过滤器中,才去查询缓存,如果没查询到,则穿透到db。如果不在布隆器中,则直接返回。
    • WEB拦截器,如果相同请求则拦截,防止重复被攻击。用户第一次请求,将请求参数放入布隆过滤器中,当第二次请求时,先判断请求参数是否被布隆过滤器命中。可以提高缓存命中率。Squid 网页代理缓存服务器在 cache digests 中就使用了布隆过滤器。Google Chrome浏览器使用了布隆过滤器加速安全浏览服务
    • Venti 文档存储系统也采用布隆过滤器来检测先前存储的数据。
    • SPIN 模型检测器也使用布隆过滤器在大规模验证问题时跟踪可达状态空间。

    代码实现 

    1. import java.util.BitSet;
    2. public class MyBloomFilter {
    3. /**
    4. * 位数组的大小
    5. */
    6. private static final int DEFAULT_SIZE = 2 << 24;
    7. /**
    8. * 通过这个数组可以创建 6 个不同的哈希函数
    9. */
    10. private static final int[] SEEDS = new int[]{3, 13, 46, 71, 91, 134};
    11. /**
    12. * 位数组。数组中的元素只能是 0 或者 1
    13. */
    14. private BitSet bits = new BitSet(DEFAULT_SIZE);
    15. /**
    16. * 存放包含 hash 函数的类的数组
    17. */
    18. private SimpleHash[] func = new SimpleHash[SEEDS.length];
    19. /**
    20. * 初始化多个包含 hash 函数的类的数组,每个类中的 hash 函数都不一样
    21. */
    22. public MyBloomFilter() {
    23. // 初始化多个不同的 Hash 函数
    24. for (int i = 0; i < SEEDS.length; i++) {
    25. func[i] = new SimpleHash(DEFAULT_SIZE, SEEDS[i]);
    26. }
    27. }
    28. /**
    29. * 添加元素到位数组
    30. */
    31. public void add(Object value) {
    32. for (SimpleHash f : func) {
    33. bits.set(f.hash(value), true);
    34. }
    35. }
    36. /**
    37. * 判断指定元素是否存在于位数组
    38. */
    39. public boolean contains(Object value) {
    40. boolean ret = true;
    41. for (SimpleHash f : func) {
    42. ret = ret && bits.get(f.hash(value));
    43. }
    44. return ret;
    45. }
    46. /**
    47. * 静态内部类。用于 hash 操作!
    48. */
    49. public static class SimpleHash {
    50. private int cap;
    51. private int seed;
    52. public SimpleHash(int cap, int seed) {
    53. this.cap = cap;
    54. this.seed = seed;
    55. }
    56. /**
    57. * 计算 hash 值
    58. */
    59. public int hash(Object value) {
    60. int h;
    61. return (value == null) ? 0 : Math.abs(seed * (cap - 1) & ((h = value.hashCode()) ^ (h >>> 16)));
    62. }
    63. }
    64. }
    65. class too{
    66. public static void main(String[] args) {
    67. String value1 = "https://javaguide.cn/";
    68. String value2 = "https://github.com/Snailclimb";
    69. MyBloomFilter filter = new MyBloomFilter();
    70. System.out.println(filter.contains(value1));
    71. System.out.println(filter.contains(value2));
    72. filter.add(value1);
    73. filter.add(value2);
    74. System.out.println(filter.contains(value1));
    75. System.out.println(filter.contains(value2));
    76. }
    77. }

    测试结果

     

  • 相关阅读:
    Windows系统的——终端命令行进入文件夹、打开程序或文件、返回路径、切换磁盘、查看路径包含的所有内容和配置环境变量操作
    JAVA大型OA协同办公系统源码【源码免费分享】
    C专家编程 第11章 你懂得C,所以C++不再话下 11.7 如何调用成员函数
    一、osg编译
    【学懂数据结构】数据结构绪论
    【无标题】
    typescript:类型放宽
    外刊30篇合集
    Kingbase备份与还原及表的约束(Kylin)
    API接口随心搭,自由定制你的数据流
  • 原文地址:https://blog.csdn.net/m0_52012606/article/details/126144901