详解布隆过滤器及其模拟实现

目录
布隆过滤器
引入
概念
工作原理
模拟实现布隆过滤器
哈希函数集
布隆过滤器基本框架
add函数(添加到布隆过滤器中)
contains函数(判断是否存在该值)
完整代码
布隆过滤器的删除
布隆过滤器的误判率
布隆过滤器的优点
布隆过滤器的缺点
布隆过滤器的应用场景
布隆过滤器
引入
我们如何判断一个元素是否在一个集合中?
我们可能会想到将集合中所有的元素加载到内存中,并存储到哈希表中,这样就能很容易判断出一个元素是否在一个集合中,但是这只能处理集合元素数量并不大的场景,对于集合中有海量的元素时,是行不通的,那么该如何解决呢?
我们知道,使用哈希表来存储,优点是能够快速查找,缺点是浪费空间;使用位图来存储,优点是能够快速查找,也不浪费空间,但是缺点是一般只能处理整形,对于较复杂的内容就无法处理了。
将哈希表和位图相结合,得到了一种新的结构,即“布隆过滤器”,它能够解决掉上面的问题。
概念
布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,由Burton Howard Bloom在1970年提出。它主要用于判断一个元素是否可能属于某个集合,而不支持直接获取集合中的所有元素。布隆过滤器的基本结构是一个固定长度的位数组/位图(Bit Array)和一组哈希函数(Hash Functions)。它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。
概念图:

工作原理
(1)初始时,位图中所有位置的值设置为0.

(2)当有值通过哈希函数映射到该位置时,值才置为1。
(3)通过判断某个值哈希映射得到的每个位置的值是否不为0,就能知道该值“一定不存在”或者“可能存在”。
模拟实现布隆过滤器
哈希函数集
class SimpleHash {public int cap;//当前容量public int seed;//随机public SimpleHash(int cap,int seed) {this.cap = cap;this.seed = seed;}//根据seed不同 创建不能的哈希函数int hash(String key) {int h;//(n - 1) & hashreturn (key == null) ? 0 : (seed * (cap-1)) & ((h = key.hashCode()) ^ (h >>> 16));}}
上面的哈希函数参照了HashMap的原码:

布隆过滤器基本框架
public class MyBloomFilter {public static final int DEFAULT_SIZE = 1 << 20;//位图public BitSet bitSet;public static final int[] seeds = {5,7,11,13,27,33};public SimpleHash[] simpleHashes;public MyBloomFilter() {bitSet = new BitSet(DEFAULT_SIZE);simpleHashes = new SimpleHash[seeds.length];for (int i = 0; i < simpleHashes.length; i++) {simpleHashes[i] = new SimpleHash(DEFAULT_SIZE,seeds[i]);}}
}
add函数(添加到布隆过滤器中)
将要添加的值使用若干哈希函数进行映射,并将映射位置的值置为1.
public void add(String val) {//让若干个哈希函数 分别处理当前的数据for (SimpleHash simpleHash : simpleHashes) {int index = simpleHash.hash(val);//把他们 都存储在位图当中即可bitSet.set(index);}
}
contains函数(判断是否存在该值)
将要添加的值使用若干哈希函数进行映射,并以此判断这些位置的值是否为0,若存在为0的情况,则该值一定不存在,否则,是可能存在,因为可能存在其他值映射到这些位置的情况。
public boolean contains(String val) {for (SimpleHash simpleHash : simpleHashes) {int index = simpleHash.hash(val);//只要有1个为 0 那么一定不存在boolean flg = bitSet.get(index);if(!flg) {return false;}}return true;
}
完整代码
import java.util.BitSet;class SimpleHash {public int cap;//当前容量public int seed;//随机public SimpleHash(int cap,int seed) {this.cap = cap;this.seed = seed;}//根据seed不同 创建不能的哈希函数int hash(String key) {int h;//(n - 1) & hashreturn (key == null) ? 0 : (seed * (cap-1)) & ((h = key.hashCode()) ^ (h >>> 16));}}
public class MyBloomFilter {public static final int DEFAULT_SIZE = 1 << 20;//位图public BitSet bitSet;public static final int[] seeds = {5,7,11,13,27,33};public SimpleHash[] simpleHashes;public MyBloomFilter() {bitSet = new BitSet(DEFAULT_SIZE);simpleHashes = new SimpleHash[seeds.length];for (int i = 0; i < simpleHashes.length; i++) {simpleHashes[i] = new SimpleHash(DEFAULT_SIZE,seeds[i]);}}public void add(String val) {//让若干个哈希函数 分别处理当前的数据for (SimpleHash simpleHash : simpleHashes) {int index = simpleHash.hash(val);//把他们 都存储在位图当中即可bitSet.set(index);}}public boolean contains(String val) {for (SimpleHash simpleHash : simpleHashes) {int index = simpleHash.hash(val);//只要有1个为 0 那么一定不存在boolean flg = bitSet.get(index);if(!flg) {return false;}}return true;}public static void main(String[] args) {MyBloomFilter myBloomFilter = new MyBloomFilter();myBloomFilter.add("hello");myBloomFilter.add("hello2");System.out.println(myBloomFilter.contains("hello"));System.out.println(myBloomFilter.contains("hello3"));}
}
运行结果:

布隆过滤器的删除
布隆过滤器不支持直接删除,因为在删除一个元素时,可能会影响其它元素。

例如,通过上图可以看到,baidu和tencent有相同的映射位置,因此如果直接删除某个元素,可能会影响到其它元素。
那么有没有办法使得布隆过滤器支持删除操作呢?
有,比如给每个比特位附带一个计数器,当有元素映射到该位置时,该位置的计数器进行++,当删除元素时,只需要将对应位置的计数器进行- -。

缺陷:
1.无法确认元素是否真的在布隆过滤器中,即可能会判断失误。
2.存在计数回绕,即溢出。
布隆过滤器的误判率
n:布隆过滤器最大处理的元素的个数
P:希望的误差率
m:布隆过滤器的bit位数目
k:哈希函数的个数

布隆过滤器的优点
1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
2. 哈希函数相互之间没有关系,方便硬件并行运算
3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能
6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算
布隆过滤器的缺点
1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)
2. 不能获取元素本身
3. 一般情况下不能从布隆过滤器中删除元素
4. 如果采用计数方式删除,可能会存在计数回绕问题
布隆过滤器的应用场景
1.缓存穿透防护:在分布式缓存系统如Redis或Memcached中,用于避免缓存穿透问题。当一个请求试图访问数据库中的某个不存在的键时,如果直接去数据库查询会增加数据库压力。通过在前端部署一个布隆过滤器,可以预先判断该键很可能不存在于数据库中,从而避免对数据库发起无效请求。
2.URL去重:在爬虫抓取网页或者日志分析中,用于URL去重,确保不会重复抓取相同的页面或记录。
3.大数据处理:在Hadoop等框架中,用来过滤掉重复的数据块或者记录,减少计算和存储负担。
4.垃圾邮件过滤:在电子邮件系统中,用于快速判断收到的邮件是否可能来自已知的垃圾邮件发送者。
5.异常事件检测:当大量事件流经系统时,可以用于快速识别并过滤出已知异常事件,降低报警系统误报率。
6.个性化推荐:在个性化推荐系统中,用于快速排除用户已经浏览过或者不感兴趣的内容。
数据库辅助索引:对于大型数据库,可以利用布隆过滤器作为辅助索引结构,提前过滤掉大部分肯定不在结果集中的查询条件,减轻主索引的压力。
7.内容检测:在社交网络中,用于快速检测用户上传的内容是否存在违规信息,或是检查用户ID、账号是否存在黑名单中。
- 欢迎大家来访问我的博客主页----》点击链接
- 欢迎大家订阅我的数据结构专栏----》点击链接
相关文章:
详解布隆过滤器及其模拟实现
目录 布隆过滤器 引入 概念 工作原理 模拟实现布隆过滤器 哈希函数集 布隆过滤器基本框架 add函数(添加到布隆过滤器中) contains函数(判断是否存在该值) 完整代码 布隆过滤器的删除 布隆过滤器的误判率 布隆过滤器的…...
element-plus中DatePicker 日期选择器组件的使用
1.选择某一天 代码: <el-date-pickerv-model"invoice_date"type"date"placeholder"请选择日期"style"width: 200px;"clearable /> 运行效果: 问题所在:这个数据的格式不是我们后端需要的那种&…...
SvelteKit 最新中文文档教程(4)—— 表单 actions
前言 Svelte,一个语法简洁、入门容易,面向未来的前端框架。 从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1: Svelte …...
力扣hot100二刷——二叉树
第二次刷题不在idea写代码,而是直接在leetcode网站上写,“逼”自己掌握常用的函数。 标志掌握程度解释办法⭐Fully 完全掌握看到题目就有思路,编程也很流利⭐⭐Basically 基本掌握需要稍作思考,或者看到提示方法后能解答⭐⭐⭐Sl…...
企业安全——数据泄露防护
0x00 前言 本篇对数据泄露防护方面的内容进行汇总和总结,为抛砖引玉的内容 0x01 数据隔离 数据隔离是防止数据泄露的一个非常好的方式,通常的隔离方式有 主机/服务器隔离网络隔离介质隔离 0x02 数据丢失预防 主要防止数据丢失的方法就是DLP&#x…...
字符串哈希从入门到精通
一、基本概念 字符串哈希是将任意长度的字符串映射为固定长度的哈希值(通常为整数)的技术,核心目标是实现O(1)时间的子串快速比较和高效查询。其本质是通过数学运算将字符串转换为唯一性较高的数值,例如: …...
C语言:编程设计猜数游戏
先由计算机想一个数给用户猜,如果猜对了,提示“right!”,猜错了,提示“wrong!及大小” 思路:用随机函数rand()取到计算机想的数 代码: #include <stdio.…...
地下车库智能停车位指引系统方案(还有缺陷)
一、系统核心技术架构 通过车牌识别+车位检测+室内定位+路径规划四大技术模块实现全自动指引: 二、关键技术方案 1. 车辆身份识别与入场触发 车牌识别(LPR)技术 入口处部署高清摄像头,自动识别车牌并关联车辆信息(如会员、临时车)。触发逻辑:识别成功后抬杆放行,同时…...
Docker 使用指南
Docker 是一种开源的容器化平台,它通过使用容器来进行应用程序的打包、分发和部署。下面是 Docker 的基本概念和优势: 容器化:Docker 使用容器来封装应用程序及其所有依赖项,使其能够在任何环境中运行,并且与底层系统隔…...
win10 c++ VsCode 配置PCL open3d并显示
win10 c VsCode配置PCL open3d并显示 一、效果图二、配置步骤2.1 安装vscode2.2 pcl-open3d配置2.3 vscode中设置 三、测试代码四、注意事项及后续 一、效果图 二、配置步骤 2.1 安装vscode vscode下载链接 下载中文插件、c相关插件 2.2 pcl-open3d配置 1)下载…...
Vala 开发环境搭建
介绍 Vala 是一种使用现代高级抽象的编程语言,与用 C 语言编写的应用程序和库相比,没有施加额外的运行时要求,也不需要使用不同的 ABI。 Vala 使用 GObject 类型系统,并具有额外的代码生成例程,使面向 GNOME 堆栈变得简…...
【网页】自制流光卡片
概述 小红书有个博主自己搞的笔记排版工具叫“流光卡片”,类似的还有个Markdown排版工具叫MD2Card。 我这个版本类似,但是自己写的东西,控制性更好。 初期就写了个静态页面,后期结合Godot快速生成,并可能结合JS库&a…...
【数据结构】栈与队列:基础 + 竞赛高频算法实操(含代码实现)
什么是栈?什么是队列? 什么是先进后出?什么是先进先出? 了解基础之后,又如何用来写算法题? 带着这些疑问,让我带领你,走进栈与队列的世界 栈与队列 栈: 1、栈的基本…...
CSP-J/S冲奖第18天:真题解析
解题步骤 读取输入:首先读取整数n,然后读取n个正整数并存储在一个数组或容器中。 排序数组:对数组进行排序,以便后续使用双指针法高效查找。 遍历数组:对于每个数target,检查是否存在另外两个不同的数a和…...
【linux】虚拟机执行sudo yum isntall perl报错 could not retrieve mirrorlist htt:
项目场景: 提示:虚拟机安装拓展包,sudo yum install perl Virtualbox 在不安装增强功能扩展的情况下, 无法自适应分辨率和共享剪切板等操作 问题描述 原因分析: 提示:这里填写问题的分析: 出现这个错误是因…...
旅游类小程序界面设计
产品概述 艾啦游是一款互联网旅游类小程序,致力于国内精品旅游,以及拥有自由行、专属热榜单、出行攻略等诸多功能,汇聚了许多国内的人气景点,与诸多城市的酒店也保持合作,打造一体式旅行服务,更有不断上新…...
DQN 玩 2048 实战|第三期!优化网络,使用GPU、Env奖励优化
视频讲解: DQN 玩 2048 实战|第三期!优化网络,使用GPU、Env奖励优化 1. 仅考虑局部合并奖励:目前的奖励只设置为合并方块时获得的分数,只关注了每一步的即时合并收益,而没有对最终达成 2048 这个…...
【python】http post 在body中传递json数据 以发送
http post 在body中传递json数据 以发送,json的格式非常重要这里要传递json对象,而不是一个json字符串 传递post一个 JSON 字符串 是ok的 是的, {"rsource_rhythm_action_list": {"name": "AI_\\u6708\\u4eae\\u…...
Linux错误(2)程序触发SIGBUS信号分析
Linux错误(2)之SIGBUS错误分析 Author: Once Day Date: 2025年3月12日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可参考专栏: Linux实践记录_Once_day的博…...
【Halcon】灰度不均解决方案
目录 1、平场校正 2、形态学背景估计 3、频域滤波抑制低频光照不均 4、动态局部自适应 1、平场校正 原理:通过白场(White Image)和黑场(Black Image)图像,手动计算校正系数 * 读取图像 read_image(ImageRaw, raw_image) // 原始图像 read_image(ImageWhite, …...
滑动窗口算法详解:从入门到精通
目录 引言 1. 滑动窗口算法简介 2. 滑动窗口的基本思想 3. 滑动窗口的应用场景 3.1 最大子数组和 3.2 最小覆盖子串 3.3 最长无重复字符子串 4. 滑动窗口的实现步骤 5. 滑动窗口的代码示例 6. 滑动窗口的优化技巧 6.1 使用哈希表记录字符频率 6.2 使用双指针维护窗口…...
JAVA数据库技术(一)
JDBC 简介 JDBC(Java Database Connectivity)是Java平台提供的一套用于执行SQL语句的Java API。它允许Java程序连接到数据库,并通过发送SQL语句来查询、更新和管理数据库中的数据。JDBC为不同的数据库提供了一种统一的访问方式,使…...
LightGBM + TA-Lib A股实战进阶:Optuna调优与Plotly可视化详解
LightGBM TA-Lib A 股实战进阶:Optuna 调优与 Plotly 可视化详解 本文系统讲解了 LightGBM 在 A 股市场的应用,涵盖模型构建、Optuna 参数调优及 Plotly 可视化。通过实战案例,帮助读者全面掌握相关技术,提升在金融数据分析与预测…...
第二:go 链接mysql 数据库
mac mysql 安装 的步骤 mysql 安装 配制: https://juejin.cn/post/7454870544929472550 mac brew 如何安装mysql数据库 要在Mac上使用Homebrew安装MySQL数据库,请按照以下步骤操作:步骤 1: 安装Homebrew 如果你还没有安装Homebrew&a…...
QListView、QListWidget、QTableView和QTableWidget
一、概念 在Qt框架中,QListView、QListWidget、QTableView和QTableWidget都是用于显示列表或表格数据的控件。 QListView是一个基于模型-视图架构的控件,用于展示列表形式的数据。它本身并不存储数据,而是依赖于一个QAbstractListModel或其子…...
[贪心算法]-最大数(lambda 表达式的补充)
1.解析 我们一般使用的排序比较大小都是 a>b 那么a在b的前面 ab 无所谓 a<b a在b的后面 本题的排序则是 ab>ba 那么a在b的前面 abba 无所谓 ab<ba a在b的后面 2.代码 class Solution { public:string largestNumber(vector<int>& nums) {//1.先把所有…...
C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷二)
目录 1. 数组名与地址 2. 指针访问数组 3.一维数组传参本质 4.二级指针 5. 指针数组 6. 指针数组模拟二维数组 1. 数组名与地址 我们先看下面这个代码: int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p &arr[0]; 这里我们使用 &arr[0] 的方式拿到了数…...
python实现简单的图片去水印工具
python实现简单的图片去水印工具 使用说明: 点击"打开图片"选择需要处理的图片 在图片上拖拽鼠标选择水印区域(红色矩形框) 点击"去除水印"执行处理 点击"保存结果"保存处理后的图片 运行效果 先简要说明…...
使用dify+deepseek部署本地知识库
使用difydeepseek部署本地知识库 一、概述二、安装windows docker desktop1、确认系统的Hyper-v功能正常启用2、docker官网下载安装windows客户端3、安装完成后的界面如下所示 三、下载安装ollama四、部署本地deepseek五、本地下载部署dify5.1 下载dify的安装包5.2 将dify解压到…...
(C语言)指针与指针数组的使用教学(C语言基础教学)(指针教学)
指针是什么?指针怎么用?指针数组又是什么??? 想必大家刚学C语言的时候对指针可谓是十分头疼了,听也听不懂,用也不会用 下面我来用我的理解来教你指针怎么用,还你一个脑子 1.指针的…...
