BitMap数据结构梳理总结及代码实现
BitMap(位图)
BitMap定义
位图(BitMap),即位(Bit)的集合,是一个离散的数组结构,用一个bit位来标记某个元素对应的Value,而Key即是该元素;最基本的情况,使用一个bit表示一个关键字的状态(可标示两种状态0-不存在,1-存在),也可以使用2bit(表示4种状态),3bit(表示8种状态)需要根据业务场景实现。
BitMap 数据结构
数据结构:byte[],一个byte 8 bit,使用bit为单位来存储数据,可以在空间和时间双重维度提高效率。
注:也可以是 int[],long[],数据结构,分别表示32bit,64bit
一个int 4个字节 32 bit,使用 BitMap(byte[]) 一个bit 表示一个int 空间缩小32倍。
如:在Java里面一个int类型占4个字节,要对于20亿个int数据进行处理,20亿*4/1024/1024/1024≈7.5G左右,需要将尽8G的内存;BitMap(byte[])处理需要20亿/8/1024/1024/1024≈0.232G,内存不到0.5G,查询某个数据时间复杂度是o(n),校验数据时间复杂度o(1)
数组:[7,9,11,1,3,5],存储如下图
注:byte[] 下标 n/8 == n >>> 3 即向右位移3位;byte[]值域 1<< n%8 = ( n & 0x07) 即取模向左位移
BitMap 应用场景
1.多个超大文件取交集(数字文件)
2.从海量电话号码中,统计不同号码的个数
3.统计日活,访问量,如 redis.setbit('YYYY-MM-DD',Id,1);
4.BitMap处理大量数字型数据的排序、查询、去重
5.BitMap扩展——Bloom Filter(布隆过滤器)过滤数据(id)
BitMap 优缺点
优点:
1.运算效率高,不需要进行比较和移位;
2.占用内存少,比如: 20亿/8/1024/1024/1024≈0.232G
3.高效排序,去重,校验o(1),查询o(n)
4.可删除(存储数值型数据)
缺点:
1.所有的数据不能重复。即不可对重复的数据进行排序和查找。
2.只有当数据比较密集时才有优势
3.数据碰撞。比如将字符串hash映射到 BitMap 的时候会有碰撞的问题,那就可以考虑用 Bloom Filter 来解决,Bloom Filter 使用多个 Hash 函数来减少冲突的概率。
4.数据稀疏。比如要存入(9,55555555,99999999)这三个数据,需要建立一个 99999999/8+1 长度的 BitMap ,但是实际上只存了3个数据,这时候就会很大的空间浪费(少量数据不建议使用);可以通过引入 Roaring BitMap 来解决。
BitMap 手动实现源码逻辑
本次底层是数据结构使用byte[],实现,也可以使用 int[],long[]数据结果,注意初始化数组大小,防止内存不够
/** * java运算符 与(&)、非(~)、或(|)、异或(^) * BitMap 数据存储约 160 亿 * 存储方式为离散数组方式,建议大量连续数据使用 * @author ysf * */ public class BitMap { private static final int MAXSIZE = Integer.MAX_VALUE-Short.MAX_VALUE; private byte[] bits; private long count=0; public BitMap() { bits = new byte[Integer.MAX_VALUE/2]; } public BitMap(long maxNum) { long maxSize = (maxNum>>>3)+1; if(maxSize > Long.valueOf(MAXSIZE)){ bits = new byte[MAXSIZE]; }else{ bits = new byte[(int)maxSize]; } } public BitMap(int count) { bits = new byte[count]; } /** * 添加数据元素,每一bit标识一个数据(0-不存在,1-存在) * @param i */ public void add(long i){ // 取模 int r = (int)(i>>3); // 取余 int c = (int)(i & 0x07); // | 或运算 只要一个为1,即为1 if(contains(i)){ return; } bits[r] |= (1 << c); count++; } /** * 验证数据是否存在 * @param i * @return */ public boolean contains(long i){ // 取模 int r = (int) (i>>3); // 取余 int c = (int)(i & 0x07); // & 与运算 两个都为1,即为1 if (((bits[r] >>> c) & 1) == 1) { return true; } return false; } /** * 移除数据 * @param i */ public void remove(long i){ // 取模 int r = (int) (i>>3); // 取余 int c = (int)(i & 0x07); if(!contains(i)){ return; } bits[r] &= ~(1 << c); count--; } /** * 获取数据记录总数 bit * @return */ public long getCount(){ return count; } /** * 获取下标元素数据,因为离散型数组存储,需要遍历全部数据 * @param subscript * @return long */ public long getNum(long subscript){ long size = (long)bits.length<<3; long sub = 0; for(long i= 0;i < size;i++){ if(contains(i)){ if(sub == subscript){ return i; } sub++; } } return 0; } } |
BitMap Bloom Filter扩展
注:布隆过滤器是以BitMap基本原理实现的扩展
布隆过滤器是一个含有 m 个元素的位数组(元素为0或1),每一位初始为0;并含有 k 个独立的哈希函数 h1, h2,..., hk 。
需要将集合中的元素加入到布隆过滤器中,依次计算h1(x), h2(x),...,hk(x),其计算结果填充对应数组的位置,并将其全部置1。一个位置可以被多次置1,但只有一次有效。
当查询某个元素是否在集合中时,计算这 k 个哈希函数,只有当其计算结果全部为1时,则认为可能存在;只要有1个计算结果为0时,则必不存在。
注:布隆过滤器存在假阳性的可能,即当所有哈希值都为1时,该元素也可能不在集合内,但该算法认为在里面。假阳性出现的概率被哈希函数的数量、位数组大小、以及集合元素等因素决定
BitMap Bloom Filter 优缺点与BitMap大致一样
- 优点:
- 空间效率高,所占空间小。
- 查询时间短。
- 自带去重
- 缺点:
- 元素添加到集合中后,不能被删除。
- 有一定的误判率
布谷鸟过滤器与布隆过滤器比较
- 支持动态删除项;
- 更好的查找性能;
- 对于需要低假阳性率的应用程序(ϵ<3%)有更好的空间利用率
BitMap Bloom Filter 使用场景
- 大量数据中判断某个数据是否存在
- 解决缓存穿透
- 爬虫url/ 邮箱等系统的过滤
注:Bloom Filtet 实现请参考google,hutool
<dependency>
<groupId>com.google</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.12</version>
</dependency>
BitMap数据结构梳理总结及代码实现相关推荐
- 数据结构梳理(6) - 图
前言 这段时间偷懒了,上次二叉树写完之后,很长时间又没更新博客了,也没学啥东西,就一直咸鱼,所以今天赶紧脱离舒适区,继续把数据结构梳理完,目前为止,已经梳理了线性表.链表.栈.队列.二叉树,这次轮到图 ...
- 【Android 内存优化】Bitmap 硬盘缓存 ( Google 官方 Bitmap 示例 | DiskLruCache 开源库 | 代码示例 )
文章目录 一.Google 官方 Bitmap 相关示例参考 二.磁盘缓存类 DiskLruCache 三.磁盘缓存初始化 四.存储数据到磁盘缓存中 五.从磁盘缓存中读取数据 六. Android 1 ...
- 如何梳理陌生的代码模块
如何梳理陌生的代码模块 总体上可以遵循从整体到局部的方式.当然,不同的工作需要对代码梳理的程度不同,根据需要考量. 下面介绍梳理代码的维度: 1.明确模块的定位 在整个系统中处于什么位置:被谁调用,提 ...
- 数组数据结构的使用与代码编写(一)
数组数据结构的使用与代码编写(一) 数组的使用 js中的数组 一.属性 1.length设置或返回数组元素的数目. 2.constructor返回构造函数的引用 var a=["a" ...
- 严蔚敏《数据结构》的全部代码实现(C语言)
严蔚敏<数据结构>的全部代码实现(C语言)源码全部都在! http://u.115.com/file/f710d0eca1 提取码 f710d0eca1
- 【SA8295P 源码分析】05 - SA8295P QNX Host 上电开机过程 进一步梳理(结合代码)
[SA8295P 源码分析]05 - SA8295P QNX Host 上电开机过程 进一步梳理(结合代码) 一.APPS PBL(Application Primary Boot Loader):固 ...
- 数据结构|考试大纲知识点梳理--自考课程代码02331 (2012年版)|计算机及应用(本科段)
本文主要按照本书的考核知识点与考核要求,对每个章节的知识点分为识记.领会.简单应用.综合应用.重点五个层次进行梳理. 注:图中标有底色且为红色字体的,为该章的重点. 后续会按照自考考试大纲,发布计算机 ...
- 非常全面的Android Bitmap知识点梳理
在日常开发中,可以说和Bitmap低头不见抬头见,基本上每个应用都会直接或间接的用到,而这里面又涉及到大量的相关知识. 所以这里把Bitmap的常用知识做个梳理,限于经验和能力,不做太深入的分析. 1 ...
- 【Android 内存优化】Bitmap 内存缓存 ( Bitmap 内存复用 | 弱引用 | 引用队列 | 针对不同 Android 版本开发不同的 Bitmap 复用策略 | 工具类代码 )
文章目录 一.Bitmap 复用池 二.弱引用 Bitmap 内存释放 三.从 Bitmap 复用池中获取对应可以被复用的 Bitmap 对象 1.Android 2.3.3(API 级别 10)及以 ...
最新文章
- springboot 日志 log4j
- java 039 s rule_Java开发架构篇:DDD模型领域层决策规则树服务设计
- DPDK之makefile编译选项修改和gdb调试(三)
- Dalvik指令集 (smali汇编)
- Android 自定义View,自定义属性--自定义圆形进度条(整理)
- 利用for循环调用插入方法批量插入 一条失败_算法与数据结构(1):基础部分——以插入排序为例...
- java中使用okhttpsoap,Android okHttp网络请求之Retrofit+Okhttp+RxJava组合
- python语言网课答案_Python语言答案
- 修改图层的范围_PS教程连载第47课:PS图层操作案例星球
- 学python前要学c吗_目前python熟练 ,想再学golang 需要先学c吗?
- 【Java】Hello world
- 览沃livox_大疆览沃浩界(Livox Horizon)激光雷达测评
- python生成图文并茂的pdf--财务报表(三)--页面布局和排版
- 京东开始卖钻石了!?
- pwnable.kr第二遍---mistake
- echarts 地图自定义图标_echarts自定义图标的点击事件怎么添加
- 【MQTT基础篇(七)】MQTT主题
- scp 保留文件属组_scp传输文件的权限问题
- Windows窗口消息大全,全不全自己看
- zedgraph显示最小刻度_ZedGraph 控件各属性以及示例