虽然bitSet取名有set,但确实不是Set的子集
public class BitSet implements Cloneable, java.io.Serializable
本来想去找个图片,结果没找到,简单点。
如果有一个Set 里面存放了(整数)1,3,4,5,23,23,12,65这些数,如果直接存储需要8×32位 ;
如果采用位图,只需要用两个long整型串联。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
|
|
|
|
|
|
|
|
一个位置表示这个数的存储。
比如1,第一个long的第2个位为1
比如4,第一个long的第2个位为1
比如65,第二个long的第2个位为1
所以数据存储时x:
long []
value
value
[x/64] =
value
[x/64]
| 1L<<x
value[x/64] 表示数据将放在第几个long 中, 因为1l 是以64为周期循环移动,会得到具体的存放位置。
x/64 = x>>6
查看Java 的bitSet的源码:
实际存放的位置
private long[] words;
初始长度为1
public BitSet() { initWords(BITS_PER_WORD); sizeIsSticky = false; }
添加数据。添加数据会
public void set(int bitIndex){
if(bitIndex <0)
- throw new IndexOutOfBoundsException("bitIndex < 0: "+ bitIndex);
//这里执行的是 bitIndex >> 6;
int wordIndex = wordIndex(bitIndex);//看看是否需要扩充。
expandTo(wordIndex);
words[wordIndex]|=(1L<< bitIndex);//Restores invariants
checkInvariants();
}
这里扩展成倍数扩展,没有优化
private void ensureCapacity(int wordsRequired) { if (words.length < wordsRequired) { // Allocate larger of doubled size or required size int request = Math.max(2 * words.length, wordsRequired); words = Arrays.copyOf(words, request); sizeIsSticky = false; } }
其他几个重要操作:add 直接是long的& ,比单个位操作要快,
public void and(BitSet set) { if (this == set) return; while (wordsInUse > set.wordsInUse) words[--wordsInUse] = 0; // Perform logical AND on words in common for (int i = 0; i < wordsInUse; i++) words[i] &= set.words[i]; recalculateWordsInUse(); checkInvariants(); }
其他还有xor,or 等。
不管怎么,bitset 初步解决了数据存储问题。但是还有很多优化问题,
比如:1)需要存储的数据只是一个1亿,那么也需要用1亿/64个long表示。
再比如 连续的 1,2,3,4,5,6 。。。1000.. 也需要存储1000/64个整数。
对于第一种 直接用ArrayList 比 bitset 节约空间。
第二种,采用run of length 比较好。比如开始位 1,重复999 . 所以只需要存储,1,999 就可以了,如果还有,
2000-4000 , 可以是,1,999,2000,1999.
每两个为一个对,第一个为开始位置,第二个为重复次数。
还有bitset的空间分配,本直接,很暴力,直接2倍,如果数据量少没有问题,但是如果本身就有1亿了,再double 已经是个问题,所以可以分层优化。
所有的这些缺点在roaringbitmap里详细说明。可以参考我的文章