前段开发项目试就发现,一部分的代码实现存在着一些性能上的隐患。但当时忙于赶进度和因为卡发中的不稳定因素,想了许多解决方案也没有机会实施。最近,正好趁个机会进行一系列的改进。java
我在团队开发中负责开发服务器端。因此在编写业务逻辑层时,经常遇到如下这样的业务逻辑:
1. 判断一个用户是否为在本身的好友列表中
2. 判断一条动态是否已被用户翻阅
3. 判断两个用户的标签的匹配度
4. .....等等
这些状况,我以前的方案是采用数据库来解决,为每条记录添加标记,须要查询时则遍历返回相应的集合。数据库
可是随着用户量的不断增多、各个用户之间的关系不断地增长、以及用户使用软件的一系列行为中这些状况是很是频繁的,这样频繁遍历大量的记录的读操做会给数据库带来难以承受的压力。
数组
那么如何需找一种更好的解决方案?
既能减小数据库须要遍历的记录数量且快速索引,又能用少许的内存表示大量的数据。
其实若是咱们对这一类型的业务逻辑进行抽象,能够获得:本质上就是判断一个元素是否存在于集合中
因此咱们能够采用位数组,经过数组的下标能快速地定位某个元素,用bit表示相应的内容可以节省大量的空间。
服务器
可是这样结构依旧不够完美,若是数据量相对较少,数组中会存在大量的无用数据, 如长度为1024的byte数组中的只有少许位被表示为1,大量位依然是0。
此时咱们能够采用游程编码压缩byte数组。如上图的游程编码后的结果能够表示为[3, 0, 2, 0, 2, 0, 1, 0 ]数据结构
Bitmap:被设计为一种用bit数组来储存表示2种状态的紧凑、快速索引的数据结构(固然Java的util包中也实现这类型的数据结构—BitSet(不过并非Set))app
其实说开来,Bitmap就是一个位数组而已,有着快速访问优点(下标访问),以及极小占用(用1bit来表示)post
有点美中不足的是,Java中并无提供bit这样的数据类型,即使是最小的数据类型byte也要占用8bit。这样就须要进行一些位运算来完成相应的操做,使得代码变得稍微复杂。
1. BitMap的内部经过byte数组实现
2. BitMap的基本操做:增删改查性能
void Set(int position); /* 将某位置"1" */
boolean Get(int position); /* 判断某位的值 */
void Clear(int postion); /* 将某位置"0" */
废话很少说,直接看图
主要分为两个步骤:
1. 先将一个byte类型的”1”左移4位,获得结果
2. 再进行简单的或运算,获得结果并覆盖原来的值ui
理解了上面的例子,相信这个应该就很简单了
一样是两步:
1. 先将一个byte类型的”1”左移3位,获得结果
2. 再进行与操做,获得结果并覆盖原来的值this
或许这里会有些疑问,为何不考虑用boolean?
首先,Java规范中没有强制规定boolean所占内存的大小。并且大部分计算机容许分配的最小内存单元为8bit
大多能够运用的场景主要是两个方面:
这里以标签匹配为例子,开发中一个用户与各个用户之间的标签匹配度是使人头疼的问题,经过匹配标签字符串或者标签ID,这样的效果都不能太让人满意,在数据库中的保存也颇为麻烦。
假如,每一个用户都有一个这样小小的长度为40的byte数组,那么用户就能够用它来表示320种标签。并且可以快速的查询,经过bitarray[tag_id]这样的访问方式能够极快查到,用户是否选取了这个标签,可以快速地计算与各个用户之间的标签匹配度
那么像第一点说的那样,长度为40的byte数据即可以保存320种标签信息,但它内存大小只有40B。并且这仍是没有进行游程编码压缩以前的大小
/** * 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.length; 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 << ofs); } public boolean getBit(int position){ if(position > size) return false; int idx = index(position); int ofs = offset(position); byte tmp = (byte)(bitArray[idx] & (1 << ofs)); 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(); } }