业务需求需要一种类似redis的bitmap的数据结构,java中有bitArray,但是功能不够全,需要一种移位操作,即,每一秒都把BitArray右移一位,下面来实现,整体感觉跟hashmap arraylist都差不多,顺便多写点注释:
其实经过测试可以知道,对于实现随着时间每一秒推移追加一个bit位的操作,历史记录时间在60s内最好用long的移位直接实现,简单也最高效,但超过long的64位上限就不行了,其次是boolean[]不停的arrayCopy和set,速度慢一点空间占用多一点,最后才是下文代码中的tailSet方法,复杂度高慢的明显但占用空间少。需要综合考虑场景,最好用long直接实现
即:
long l = (l<<1)|1;
代表了时间过了1s,新来的数据标记为1,表示存在数据,超级简单快
//可以继承security包下的BitArray,很多方法用父类,简介
//这里便于理解,部分简单方法直接从源码copy,不继承
//新增了一个成员变量capacity,因为移动意味着实际容量会减少
public class BitArray1 {
// 存储真正的二进制数据
private byte[] repn;
private int length; //整个数据结构的长度
private int capacity;//当前已使用的容量
public BitArray1(int var1) throws IllegalArgumentException {
if (var1 < 0) {
throw new IllegalArgumentException("Negative length for BitArray");
} else {
this.length = var1;
this.repn = new byte[(var1 + 8 - 1) / 8];
}
}
// subscript下标,返回这个位置应该有多少个整的byte
private static int subscript(int var0) {
return var0 / 8;
}
//返回var0 在一个byte内应处的位置,从右往左走的,也就是一个byte内的应有的二进制表示
private static int position(int var0) {
return 1 << 7 - var0 % 8;
}
//var1 要放置的位置,var2放置true还是false
/**
* copied from BitArray source code
*
* @param var1 位置
* @param var2 true false
* @throws ArrayIndexOutOfBoundsException
*/
public void set(int var1, boolean var2) throws ArrayIndexOutOfBoundsException {
// 容量范围内
if (var1 >= 0 && var1 < this.length) {
int var3 = subscript(var1);//从前面数第几个完整的byte
int var4 = position(var1);//最后一个不完整byte 从前面数的位置
byte[] var10000;
if (var2) {
var10000 = this.repn;
var10000[var3] = (byte) (var10000[var3] | var4);// 取出对应区域8位byte表示,var4肯定只有一个1,或运算,将对应位置置为1
} else {
var10000 = this.repn;
var10000[var3] = (byte) (var10000[var3] & ~var4);
// 取反,var4目标位置变为0,其他位置变为1,与上对应位置的byte var3,
// 原有1才变成1,原有0还是0,目标位置不管原有1还是0都变成0
}
this.capacity++;
} else {
throw new ArrayIndexOutOfBoundsException(Integer.toString(var1));
}
}
/**
* 整体右移offset位,基于byte[]实现
* 如果长度不是很大的情况,使用long[]会更加简单
*
* @param offset
*/
public void rightMove(int offset) {
//实际上,右移仍然要保证this.repn的首元素byte为8个字节,而不是保证尾元素为8个字节
if (offset > 0 && offset < this.length) {
int var1 = offset / 8; // offset要移动var1个完整8位byte
// repn 从后面数,剔除 var1个byte
byte[] temp = new byte[this.repn.length - var1];
System.arraycopy(this.repn, 0, temp, 0, this.repn.length - var1);
//新的 最后byte有var3个数,下标应该截止到var3-1,也就是temp要截止到var3-1 取到
int var2 = (this.length - offset) % 8;
int var3 = ~(127 >> (var2 - 1)); // 后5位1 取反后变前面全1 后面0
temp[temp.length - 1] = (byte) (temp[temp.length - 1] & var3);
// 赋值并修改长度
this.repn = temp;
this.length -= offset;
} else {
//offset大于整体长度直接变成全0了,没有实际意义
throw new ArrayIndexOutOfBoundsException(Integer.toString(offset));
}
}
/**
* 特定用途: 左移一位,并且将最后的空位 置为b
*
* @param b
*/
public void tailSet(boolean b) {
//1.没满的时候,直接往后面加
if (this.capacity < this.length) {
this.set(this.capacity, b);
} else {
//2.满了以后,全体左移1位并set
for (int i = 0; i < this.repn.length - 1; i++) {
//2.1 遍历,每次当前byte左移,然后取下一byte首位补足
repn[i] = (byte) ((repn[i] << 1) + (repn[i + 1] & (1 << 7)));
}
//2.2 最后一byte左移,并取入参补足,注意空的地方肯定是0,所以else不用动
repn[repn.length - 1] = (byte) (repn[repn.length - 1] << 1);
if (b) {
repn[repn.length - 1] = (byte) (repn[repn.length - 1] | (1 << (8 - capacity % 8)));
}
}
}
public boolean get(int var1) throws ArrayIndexOutOfBoundsException {
if (var1 >= 0 && var1 < this.length) {
return (this.repn[subscript(var1)] & position(var1)) != 0;
} else {
throw new ArrayIndexOutOfBoundsException(Integer.toString(var1));
}
}
public boolean[] toBooleanArray() {
boolean[] var1 = new boolean[this.length];
for (int var2 = 0; var2 < this.length; ++var2) {
var1[var2] = this.get(var2);
}
return var1;
}
}
版权声明:本文为u012496112原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。