JDK11
BitMap原理&使用场景
用一个bit来存放一个状态的容器。由于对内存占用少,适合用于处理大规模数据和数据状态不多的情况。毕竟一个bit只对应两个状态。
图来自ref
假设原来有个int数组[1,2,3,6,7]需要用5*32bit=160bit来保存存储空间。
但如果把元素的值作为下标每个下标用一个bit来表示,如0表示不存在该元素,1表示存在。那么只需要在内存空间开辟一个bit大小为8的数组。
- 场景一:
O(1)的海量数据的查重。
- 场景二:
判断两个数组重复元素。原先方案需要做一个两层for循环,现在只需要两个bitMap做与操作。
- 场景三:
如果数据分布不均匀,如数组是1,100,100000000,那么为了存储100000000需要开100000001个bit,造成较大空间浪费。可以使用roaring map
- 场景四:
Bloom Filter的实现
类继承关系
public class BitSet implements Cloneable, Serializable
BitSet虽然叫做Set,在java.util包中,但事实上和Set,List这些接口一点关系都没有
重要成员
可以看到最重要BitSet本质上就是用一个long数组来存储bit的信息,默认每一个long可以存储64bit
/*
* BitSets are packed into arrays of "words." Currently a word is
* a long, which consists of 64 bits, requiring 6 address bits.
* The choice of word size is determined purely by performance concerns.
*/
private static final int ADDRESS_BITS_PER_WORD = 6;
// 每个word所占的位数:64bit,(这里所谓的word就是long类型)
private static final int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
private static final int BIT_INDEX_MASK = BITS_PER_WORD - 1;
/* Used to shift left or right for a partial word mask */
private static final long WORD_MASK = 0xffffffffffffffffL;
/**
* The internal field corresponding to the serialField "bits".
*/
/*
* 存储word,其实就是存储位集中的bit
* 在计算位的时候,需要从左到右取出word,并从右到左排列。
*/
private long[] words;
/**
* The number of words in the logical size of this BitSet.
*/
// 位集正在被使用的字的长度
private transient int wordsInUse = 0;
/**
* Whether the size of "words" is user-specified. If so, we assume
* the user knows what he's doing and try harder to preserve it.
*/
// 字的大小是否为用户指定
private transient boolean sizeIsSticky = false;
构造函数
- 默认构造函数
默认初始会为words数组分配一个元素
/**
* Creates a new bit set. All bits are initially {@code false}.
*/
// 构造可以存储64个bit的位集(即1个word)
public BitSet() {
// 由给定的bit数量,构造一个匹配大小的long数组存储bit
initWords(BITS_PER_WORD);
sizeIsSticky = false;
}
我们来看initWords,作用是根据bitSet的bit的大小来判断需要分配多少个word
/*
* 由给定的bit数量,构造一个匹配大小的long数组存储bit。
* 这里会向上取整,比如输入60,则虽然不足64位,却也构造包含一个long的word数组。
*/
private void initWords(int nbits) {
// 计算指定索引处的bit所在的word的索引
//64-1 = 1 1111
int size = wordIndex(nbits - 1);
words = new long[size + 1];
}
/**
* Given a bit index, return word index containing it.
*/
// 计算指定索引处的bit所在的word的索引
private static int wordIndex(int bitIndex) {
//1 1111 >> 6 = 0 说明 0-63bit对应的word索引为0
return bitIndex >> ADDRESS_BITS_PER_WORD;
}
其他构造函数
// 构造至少可以存储nbits个bit的位集
public BitSet(int nbits) {
// nbits can't be negative; size 0 is OK
if(nbits<0) {
throw new NegativeArraySizeException("nbits < 0: " + nbits);
}
// 由给定的bit数量,构造一个匹配大小的long数组存储bit
initWords(nbits);
sizeIsSticky = true;
}
/**
* Creates a bit set using words as the internal representation.
* The last word (if there is one) must be non-zero.
*/
// 构造可以存储words.length个word的位集,且该位集正在被使用
private BitSet(long[] words) {
this.words = words;
this.wo