Java中的布隆过滤器你真的懂了吗

 更新时间:2023年04月26日 17:15:02   作者:平凡一只涛  
经常会听到大家说起布隆过滤器,但是很多人都只是听过名字,却并不知道其是怎么实现的。下面将详细介绍一下布隆过滤器,并且使用简单的代码演示

什么是布隆过滤器

布隆过滤器(Bloom Filter)是一种空间效率非常高的随机数据结构,它利用位数组(BitSet)表示一个集合,并通过一定数量的哈希函数将元素映射为位数组中的位置,用于检查一个元素是否属于这个集合。

实现的核心思想

对于一个元素,通过多个哈希函数生成多个哈希值,将对应的位在位数组中设为 1,若多个哈希值对应的位都为 1,则认为该元素可能在集合中;若至少有一个哈希值对应的位为 0,则该元素一定不在集合中。这种方法可以在较小的空间中实现高效的查找,但可能存在误判率(false positive)。

怎么理解

一个典型的布隆过滤器包含三个参数: 位数组的大小(即存储元素的个数); 哈希函数的个数; 填充因子(即误判率),即将元素数量与位数组大小的比值。

如上图所示: 布隆过滤器的基本操作流程,包括初始化位数组和哈希函数、插入元素、检查元素是否在集合中等。其中,每个元素都会被多个哈希函数映射到位数组中的多个位置,而在检查元素是否在集合中时,需要确保所有对应的位都被设置为 1,才会认为该元素可能在集合中。

典型应用场景

垃圾邮件过滤: 将所有的黑名单邮件对应的哈希值在布隆过滤器中对应的位置设为 1,对于每一封新邮件,将其哈希值在布隆过滤器中对应的位置检查是否都为 1,若是,则认为该邮件是垃圾邮件,否则可能是正常邮件;

URL 去重: 将已经抓取的 URL 对应的哈希值在布隆过滤器中对应的位置设为 1,对于每一条新的 URL,将其哈希值在布隆过滤器中对应的位置检查是否都为 1,若是,则认为该 URL 已经抓取过,否则需要进行抓取;

缓存击穿: 将缓存中存在的所有数据对应的哈希值在布隆过滤器中对应的位置设为 1,对于每一个查询的键值,将其哈希值在布隆过滤器中对应的位置检查是否都为 1,若是,则认为该键值存在于缓存中,否则需要从数据库中查询并将其添加到缓存中。

需要注意的是,布隆过滤器的误判率会随着位数组大小的增加而减小,但同时也会增加内存开销和计算时间。 为了方便理解布隆过滤器,下面用java代码实现一个简单的布隆过滤器:

import java.util.BitSet;
import java.util.Random;
public class BloomFilter {
  private BitSet bitSet;           // 位集,用于存储哈希值
  private int bitSetSize;         // 位集大小
  private int numHashFunctions;   // 哈希函数数量
  private Random random;          // 随机数生成器
  // 构造函数,根据期望元素数量和错误率计算位集大小和哈希函数数量
  public BloomFilter(int expectedNumItems, double falsePositiveRate) {
    this.bitSetSize = optimalBitSetSize(expectedNumItems, falsePositiveRate);
    this.numHashFunctions = optimalNumHashFunctions(expectedNumItems, bitSetSize);
    this.bitSet = new BitSet(bitSetSize);
    this.random = new Random();
  }
  // 根据期望元素数量和错误率计算最佳位集大小
  private int optimalBitSetSize(int expectedNumItems, double falsePositiveRate) {
    int bitSetSize = (int) Math.ceil(expectedNumItems * (-Math.log(falsePositiveRate) / Math.pow(Math.log(2), 2)));
    return bitSetSize;
  }
  // 根据期望元素数量和位集大小计算最佳哈希函数数量
  private int optimalNumHashFunctions(int expectedNumItems, int bitSetSize) {
    int numHashFunctions = (int) Math.ceil((bitSetSize / expectedNumItems) * Math.log(2));
    return numHashFunctions;
  }
  // 添加元素到布隆过滤器中
  public void add(String item) {
    // 计算哈希值
    int[] hashes = createHashes(item.getBytes(), numHashFunctions);
    // 将哈希值对应的位设置为 true
    for (int hash : hashes) {
      bitSet.set(Math.abs(hash % bitSetSize), true);
    }
  }
  // 检查元素是否存在于布隆过滤器中
  public boolean contains(String item) {
    // 计算哈希值
    int[] hashes = createHashes(item.getBytes(), numHashFunctions);
    // 检查哈希值对应的位是否都为 true
    for (int hash : hashes) {
      if (!bitSet.get(Math.abs(hash % bitSetSize))) {
        return false;
      }
    }
    return true;
  }
  // 计算给定数据的哈希值
  private int[] createHashes(byte[] data, int numHashes) {
    int[] hashes = new int[numHashes];
    int hash1 = Math.abs(random.nextInt());
    int hash2 = Math.abs(random.nextInt());
    for (int i = 0; i < numHashes; i++) {
      // 使用两个随机哈希函数计算哈希值
      hashes[i] = Math.abs((hash1 * i) + (hash2 * i) + i) % data.length;
    }
    return hashes;
  }
}

以上就是Java中的布隆过滤器你真的懂了吗的详细内容,更多关于Java布隆过滤器的资料请关注脚本之家其它相关文章!

相关文章

  • Spring Boot修改内置Tomcat默认端口号的示例

    Spring Boot修改内置Tomcat默认端口号的示例

    本篇文章主要介绍了Spring Boot修改内置Tomcat端口号的示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • Java 图片压缩实现思路及代码

    Java 图片压缩实现思路及代码

    本文为大家详细介绍下图片压缩的具体实现思路及java代码,想学习的各位可以参考下哈,希望对大家有所帮助
    2013-07-07
  • SpringBoot中使用监听器的方法详解

    SpringBoot中使用监听器的方法详解

    这篇文章主要为大家详细介绍了SpringBoot中使用监听器的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • java 压缩图片(只缩小体积,不更改图片尺寸)的示例

    java 压缩图片(只缩小体积,不更改图片尺寸)的示例

    这篇文章主要介绍了java 如何压缩图片体积,帮助大家更好的利用Java处理图片,应对特殊情况,感兴趣的朋友可以了解下
    2020-10-10
  • Java的LinkedHashMap的实现原理详解

    Java的LinkedHashMap的实现原理详解

    这篇文章主要介绍了Java的LinkedHashMap的实现原理详解,   LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序,此实现提供所有可选的映射操作,并允许使用null值和null键,此类不保证映射的顺序,特别是它不保证该顺序恒久不变,需要的朋友可以参考下
    2023-09-09
  • Java中Stream的flatMap与map使用场景及区别详解

    Java中Stream的flatMap与map使用场景及区别详解

    这篇文章主要介绍了Java中Stream的flatMap与map使用场景及区别详解,Stream 流式操作,一般用于操作集合即 List 一类的数据结构,简单来说 Stream 的 map 使得其中的元素转为另一种元素的映射(map)方法,需要的朋友可以参考下
    2024-01-01
  • 浅谈Maven包冲突的原理及解决方法

    浅谈Maven包冲突的原理及解决方法

    这篇文章主要介绍了浅谈Maven包冲突的原理及解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • java不同线程解读以及线程池的使用方式

    java不同线程解读以及线程池的使用方式

    这篇文章主要介绍了java不同线程解读以及线程池的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • maven继承父工程统一版本号的实现

    maven继承父工程统一版本号的实现

    这篇文章主要介绍了maven继承父工程统一版本号的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Java中indexOf()方法详解及其日常使用举例

    Java中indexOf()方法详解及其日常使用举例

    这篇文章主要给大家介绍了关于Java中indexOf()方法详解及其日常使用举例的相关资料,indexOf()方法是JavaScript字符串的内置方法之一,它用于查找给定子字符串在原始字符串中第一次出现的位置,需要的朋友可以参考下
    2023-12-12

最新评论