简化布隆过滤器——BitMap
简化布隆过滤器——BitMap
前言
前段开发项目试就发现,一部分的代码实现存在着一些性能上的隐患。但当时忙于赶进度和由于卡发中的不稳定因素,想了许多解决方案也没有机会实施。最近,正好趁个机会进行一系列的改进。
我在团队开发中负责开发服务器端。所以在编写业务逻辑层时,常常遇到以下这样的业务逻辑:
1. 判断一个用户是否为在自己的好友列表中
2. 判断一条动态是否已被用户翻阅
3. 判断两个用户的标签的匹配度
4. .....等等
这些情况,我之前的方案是采用数据库来解决,为每条记录添加标记,需要查询时则遍历返回相应的集合。
但是随着用户量的不断增多、各个用户之间的关系不断地增加、以及用户使用软件的一系列行为中这些情况是非常频繁的,这样频繁遍历大量的记录的读操作会给数据库带来难以承受的压力。

那么如何需找一种更好的解决方案?
既能减少数据库需要遍历的记录数量且快速索引,又能用少量的内存表示大量的数据。
其实如果我们对这一类型的业务逻辑进行抽象,可以得到:本质上就是判断一个元素是否存在于集合中
所以我们可以采用位数组,通过数组的下标能快速地定位某个元素,用bit表示相应的内容能够节省大量的空间。

但是这样结构依旧不够完美,如果数据量相对较少,数组中会存在大量的无用数据, 如长度为1024的byte数组中的只有少量位被表示为1,大量位依然是0。
此时我们可以采用游程编码压缩byte数组。如上图的游程编码后的结果可以表示为[3, 0, 2, 0, 2, 0, 1, 0 ]
一、Bitmap介绍
Bitmap:被设计为一种用bit数组来储存表示2种状态的紧凑、快速索引的数据结构(当然Java的util包中也实现这类型的数据结构—BitSet(不过并不是Set))
二、BitMap主要原理
其实说开来,Bitmap就是一个位数组而已,有着快速访问优势(下标访问),以及极小占用(用1bit来表示)
三、BitMap的主要设计
有点美中不足的是,Java中并没有提供bit这样的数据类型,即便是最小的数据类型byte也要占用8bit。这样就需要进行一些位运算来完成相应的操作,使得代码变得稍微复杂。
1. BitMap的内部通过byte数组实现
2. BitMap的基本操作:增删改查
void Set(int position); /* 将某位置"1" */
boolean Get(int position); /* 判断某位的值 */
void Clear(int postion); /* 将某位置"0" */
Set()的实现原理
废话不多说,直接看图

主要分为两个步骤:
1. 先将一个byte类型的”1”左移4位,得到结果
2. 再进行简单的或运算,得到结果并覆盖原来的值
Get()的实现原理

理解了上面的例子,相信这个应该就很简单了
同样是两步:
1. 先将一个byte类型的”1”左移3位,得到结果
2. 再进行与操作,得到结果并覆盖原来的值
或许这里会有些疑问,为什么不考虑用boolean?
首先,Java规范中没有强制规定boolean所占内存的大小。而且大部分计算机允许分配的最小内存单元为8bit
四、可以用运用BitMap解决问题的实用场景
大多可以运用的场景主要是两个方面:
这里以标签匹配为例子,开发中一个用户与各个用户之间的标签匹配度是令人头疼的问题,通过匹配标签字符串或者标签ID,这样的效果都不能太让人满意,在数据库中的保存也颇为麻烦。
一、快速索引
假如,每个用户都有一个这样小小的长度为40的byte数组,那么用户就可以用它来表示320种标签。而且能够快速的查询,通过bitarray[tag_id]这样的访问方式可以极快查到,用户是否选取了这个标签,能够快速地计算与各个用户之间的标签匹配度
二、数据压缩
那么像第一点说的那样,长度为40的byte数据便可以保存320种标签信息,但它内存大小只有40B。而且这还是没有进行游程编码压缩之前的大小
五、Java实现
/**
* Created by auhnayuil on 17-6-7.
*/
public class BitMap {
public static final int DEFAULT_SIZE = 1024;
public static final boolean EXIST = true;
public static final boolean NULL = false;
public static final short bits = 8;
private byte[] bitArray;
private int size;
public BitMap(){
this(DEFAULT_SIZE);
}
public BitMap(byte[] bitArray){
this.size = bitArray.length * bits;
this.bitArray = bitArray;
}
public BitMap(int defaultSize) {
this.size = defaultSize * bits;
this.bitArray = new byte[defaultSize];
}
public BitMap(int size, boolean elem){
this(size);
if(EXIST == elem) {
for (int i = 0; i
bitArray[i] = (byte) ~bitArray[i];
}
}
public int size(){
return size;
}
public int index(int position){
int idx = (position + bits - 1) / bits;
return idx - 1;
}
public int offset(int position){
int ofs = position % 8;
return (ofs == 0 ? ofs : 8 - ofs);
}
public void setBit(int position){
if(position > size)
return ;
int idx = index(position);
int ofs = offset(position);
bitArray[idx] |= (byte)(1
}
public boolean getBit(int position){
if(position > size)
return false;
int idx = index(position);
int ofs = offset(position);
byte tmp = (byte)(bitArray[idx] & (1
return tmp != 0;
}
public void setBitArray(byte[] bitArray){
this.bitArray = bitArray;
}
public byte[] getBitArray(){
return bitArray;
}
public String byteToStr(int position){
byte b = bitArray[index(position)];
StringBuffer sb = new StringBuffer("");
for(int i=bits-1; i>-1; i--)
sb.append((byte)((b >> i) & 0x1));
return sb.toString();
}
}
简化布隆过滤器——BitMap的更多相关文章
- 【布隆过滤器】基于Hutool库实现的布隆过滤器Demo
布隆过滤器出现的背景: 如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定.链表.树.散列表(又叫哈希表,Hash table)等等数据结构都是这种思路,存储 ...
- 基于Java实现简化版本的布隆过滤器
一.布隆过滤器: 布隆过滤器(Bloom Filter)是1970年由布隆提出的.它实际上是一个很长的二进制向量和一系列随机映射函数.布隆过滤器可以用于检索一个元素是否在一个集合中.它的优点是空间效率 ...
- 从位图到布隆过滤器,C#实现
前言 本文将以 C# 语言来实现一个简单的布隆过滤器,为简化说明,设计得很简单,仅供学习使用. 感谢@时总百忙之中的指导. 布隆过滤器简介 布隆过滤器(Bloom filter)是一种特殊的 Hash ...
- 【转】Bloom Filter布隆过滤器的概念和原理
转自:http://blog.csdn.net/jiaomeng/article/details/1495500 之前看数学之美丽,里面有提到布隆过滤器的过滤垃圾邮件,感觉到何其的牛,竟然有这么高效的 ...
- 布隆过滤器(Bloom Filter)
一.布隆过滤器介绍 Bloom Filter是一种空间效率很高的随机数据结构,Bloom Filter可以看做是对bit-map的扩展,它的原理如下: 当一个元素被加入集合时,通过K个Hash函数将这 ...
- BloomFilter(布隆过滤器)
原文链接:http://blog.csdn.net/qq_38646470/article/details/79431659 1.概念: 如果想判断一个元素是不是在一个集合里,一般想到的是将所有元素保 ...
- C++布隆过滤器
布隆过滤器 这名词有没有听着好像很 挺高大上的,的确,它也是一种很重要的结构,下面一起看看: 一:说说历史: (Bloom Filter)是由布隆(Burton Howard Bloom)在1970年 ...
- 浅谈redis的HyperLogLog与布隆过滤器
首先,HyperLogLog与布隆过滤器都是针对大数据统计存储应用场景下的知名算法. HyperLogLog是在大数据的情况下关于数据基数的空间复杂度优化实现,布隆过滤器是在大数据情况下关于检索一个元 ...
- 布隆过滤器redis缓存
Bloom Filter布隆过滤器算法背景如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定.链表.树.散列表(又叫哈希表,Hash table)等等数据结构 ...
随机推荐
- IO流输入 输出流 字符字节流
一.流 1.流的概念 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作. ...
- 关于mpu6050的几个很好的帖子
最近在研究6050,真是很磨人啊,这个小东西还挺复杂,一个读取程序竟然需要600多行. 这几天连查资料找到了几个很好的帖子,要是以后有人看到这篇帖子,可以避免误入歧途,也可以省去很多时间. 1.阿西莫 ...
- vue实现全选效果
vue实现全选效果 接触vue快半年了,记得刚用vue做项目的时候遇到一个全选功能,当时到处百度也没有找到怎么实现,最后还是用了jquery进行dom操作实现的. 今天没事就顺手写了一个,感觉很简单, ...
- 用vuejs仿网易云音乐(实现听歌以及搜索功能)
前言 前端时间学了vue,一开始看了vue1.0,后来实在觉得技术总得实践,就直接上手vue2.0.然后花了将近一周时间做了一个网易云音乐的小项目.一开始觉得项目比较小,没必要用vuex所以就没有使用 ...
- TypeScript入门-枚举、类型推论
枚举 使用枚举可以定义一些具有名字的数字常量,和在C语言中一样都是使用关键字enum enum Direction { Up = 1, Down = 1<<2, Left, Right } ...
- Spring Cloud 学习笔记(二)——Netflix
4 Spring Cloud Netflix Spring Cloud 通过自动配置和绑定到Spring环境和其他Spring编程模型惯例,为Spring Boot应用程序提供Netflix OSS集 ...
- Android中EditText设置输入条件
一.应用场景 之前做商城应用时,会有对用户资料的设置情况进行限制,如下: (1)用户邮箱,应当只允许输入英文字母,数字和@.两个符号, (2)用户手机,应当只能输入数字,禁止输入其他字符. (3)用户 ...
- 学习MVC之租房网站(八)- 前台注册和登录
在上一篇<学习MVC之租房网站(七)-房源管理和配图上传>完成了在后台新增.编辑房源信息以及上传房源配图的功能.到此后台开发便告一段落了,开始实现前台的功能,也是从用户的登录.注册开始. ...
- Mongodb安装启动详解
最近在倒腾node+mongodb,安装mongodb的时候开始遇到很多问题,然后折腾了好几次,直到可以很顺利完成安装 ,所以把安装的过程记录下来. 线上系统基本上都是linux的,所以只安装了lin ...
- 命令模式(Command Pattern)
命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式.命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求 ...