前些日子需要写一个有限域上多项式计算的程序,考虑使用Java的BitSet来表示多项式,但Java Doc里面对各种API的表现都语焉不详,索性去看了一下java.util.BitSet的源代码。

BitSet的实现

BitSet是使用一个long类型的数组实现的,如下:

java.util.BitSet
Java
/*
* 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 final static int ADDRESS_BITS_PER_WORD = 6;
private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1;
/**
* The internal field corresponding to the serialField "bits".
*/
private long[] words;
1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* 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.
*/
privatefinalstaticintADDRESS_BITS_PER_WORD=6;
privatefinalstaticintBITS_PER_WORD=1<
privatefinalstaticintBIT_INDEX_MASK=BITS_PER_WORD-1;
/**
* The internal field corresponding to the serialField "bits".
*/
privatelong[]words;

一个long类型占8个字节,也就是64个bit。

BitSet 有两个构造函数,一个没有参数,一个需要传进去一个int类型的nbit参数。当使用有参数的构造函数时,BitSet会初始化一个能够装下nbit位的words数组,然后会把一个名为sizeIsSticky的boolean成员变量设为true。

一旦sizeIsSticky被设为true,BitSet就会认为调用者会自己维护BitSet的大小,只有在操作过程中,BitSet发现长度不够,需要调用ensureCapacity()时,才会改变words数组大小,并将sizeIsSticky设为false。

需要注意的时,BitSet的length()成员函数,返回的是下标最高的true位的下标+1,而不是BitSet实际上有多少位。所以当用上述带nBit参数的构造方法new了一个BitSet对象时,调用它的length()函数返回值是0。比如new了一个保存了10个false的BitSet,结果在后面的计算中你无法获得10这个length。

BitSet这怪脾气也导致了我的计划泡汤了,不想自己维护BitSet的实际长度的话,还是用Boolean类型的List或者Vector比较合适。

BitSet的逻辑运算

BitSet提供了许多逻辑运算符,使用起来非常方便。由于Java Doc里面没有提到进行逻辑运算时结果的长度问题,我也读了一遍这几个逻辑运算函数的实现。

b.and(BitSet set)

如果set的长度比b的长度小,那么先直接把b多出的那部分扔掉,反正and操作也会让他们变成false。然后进行and操作,结束后重新计算b的长度,即调用recalculateWordsInUse(),检查b的尾部是否有全为0的word,如果有就扔掉。

如果set的长度比b的长度大,就更不用说了,set多出的那部分长度不予考虑。

b.or(BitSet set)

直接把b的长度增加到b和set中较长者的长度,这个过程可能需要调用ensureCapacity(),然后进行or操作,此时不需要再调用recalculateWordsInUse(),因为此时的最高位肯定是1。

其他逻辑运算与and和or在长度计算上的行为比较类似,不再赘述(这个其实和数学上的逻辑运算一样)。由此可见,BitSet比较适合进行数学上的逻辑运算,而不太适合利用BitSet来表示一些含义较为复杂的对象,比如我提到的有限域上的多项式。