今天看es看到,es使用bitset来标记一个文档是否匹配,于是就看了下java.util中的BitSet实现原理,记录一下
该数据结构能够实现什么
void set(int id):设置id存在
boolean get(int id): 查看id是否存在
容易想到用bit位的1或者0来代表, 那么一个long的长度8字节(64位),能存储64个位置的状态,对于id来说需要多少个long才能存储所有的状态位呢?答案是 id / 64
使用一个长度为id/64的long数组就可以存储所有的状态位
实现思路
初始化存储数组: long[] words
- set(id)
- 计算数组下标(第几个long来存储该值的状态):id/64 = id >> 6;
- 计算该long的哪一位设置位1:将从右数第(id%64)的bit位设置为1: words[下标] |= 1 << id%64,等价于 words[id >> 6] |= 1L << id
至于为啥等价,可能比较难理解,因为计算机在计算位移操作时,遵循以下规则:
整形: int << num,由于int只有32位,左移数不能超过该值,int << num 等价于 int << num的后5位
长整形: long << num,同理long << num 等价于 long << num的后6位 - 再来看下java.util.BitSet.set的实现
public void set(int bitIndex) {
if (bitIndex < 0)
throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
int wordIndex = wordIndex(bitIndex);
expandTo(wordIndex);
//重点看这里
words[wordIndex] |= (1L << bitIndex); // Restores invariants
checkInvariants();
}
- boolean get(id)
set是将某一个bit位设置为1,get是判断某一个bit位是否为1
- 获取words数组下标,id >> 6
- 判断对应bit位是否为1, (1L << i) & words[id>>6] != 0
public boolean get(int bitIndex) {
if (bitIndex < 0)
throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
checkInvariants();
int wordIndex = wordIndex(bitIndex);
return (wordIndex < wordsInUse)
&& ((words[wordIndex] & (1L << bitIndex)) != 0);//重点看这里
}
我们这里也发现只有在数量很多的时候,bitset才能体现出威力,不当使用效率并不高。