Java BitSet 使用及部分源码学习

背景

BitSet的使用在很多场景都十分有用,例如:

------------------------------------------引用自http://www.myexception.cn/software-architecture-design/925005.html------------------------------------

问题:对40亿个数据进行排序,数据类型为 int,无相同数据。
思考:关于40亿个数据的排序,首先想如何存储呢?一个int 4个字节,也就是160亿个字节,也就是大概有16GB的数据,现在所有的计算机估计
没有这么大的内存吧,所以我们就可以文件归并排序,也可以分段读入数据在进行Qsort,但是都需要不停地读入文件,可以想象不停地读取文件硬件操作会有多么浪费时间。

我们这样都是用4个字节来存储了一个数据,在计算机里都是用二进制进行表示,
例如 5 :0000 0000 0000 0000 0000 0000 0000 0101
现在引入Bitmap,所谓Bitmap就是用一个bit来表示一个数据。平时32位存储一个数据,我们可以换一种想法,用一个字节32位来存储0-31这32个数据,例如我们对2,1,5,12这四个数据进行由小到大的排序,首先把32位初始化为0,我们可以把这4个数据存储为0000 0000 0000 0000 0001 0000 0010 0110

我们就把32位中的分别把 2  1  5  12位置为1,然后从第0位开始遍历,看相应位是否为1,为1就进行输出,就完成了数据从小到大的排序。

再返回原题应用Bitmap就可以把16GB的存储空间缩小为16GB/32 = 512M,就可以大大减少读取文件的工作。直接读一次文件存入内存,然后遍历输出就完成了排序。

优点:既大量节省了空间,又把时间复杂度降低到O(n)。
不足:如果数据过于稀疏就会有大量无用遍历,浪费时间。

------------------------------------------引用自http://www.myexception.cn/software-architecture-design/925005.html---------------------------------

在Java中BitMap的实现就是BitSet(java.util.BItSet)。

相关用法

1、构造函数  public BitSet()   以及  pulic BitSet(int size)     //默认所有位都是false,并且会自动扩展大小
2、set(int index,boolean flag)  
       以及 set(int index)   //会将index位设置为true
3、get(int index)   //得到当前位的boolean值
4、and、or、xor等位运算操作

源码

首先看构造方法:
    /**
     * Creates a new bit set. All bits are initially {@code false}.
     */
    public BitSet() {
        initWords(BITS_PER_WORD);
        sizeIsSticky = false;
    }

这里面用到了BITS_PER_WORD和initWords方法,声明如下:
    private final static int ADDRESS_BITS_PER_WORD = 6;
    private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;

    private void initWords(int nbits) {
        words = new long[wordIndex(nbits-1) + 1];
    }

wordIndex方法:
    /**
     * Given a bit index, return word index containing it.
     */
    private static int wordIndex(int bitIndex) {
        return bitIndex >> ADDRESS_BITS_PER_WORD;
    }

返回的是bitIndex右移6位的值。可以理解为除64的结果。

可以看到initWords方法创建了一个long数组,数组的大小为wordIndex(nbits-1) + 1,为什么是这个呢?
我们假设几个nbits的值:
nbits == 3   那么wordIndex返回0,数组大小为1
nbits == 64         wordIndex返回0,数组大小为1
nbits == 65         wordIndex返回1,数组大小为2
看到这里大家应该明白了,事实上BitSet使用long数组来存储每一位的boolean值的。这里long数组为words,而wordIndex则返回给定bitIndex下,在words中的位置。

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();
    }

words[wordIndex] |= (1L << bitIndex);   中,由于左移只会取低六位,所以可以不用对64取模。
其中expandTo()方法保证不会出现long数组越界,当wordIndex的值大于数组的长度时,则会自动扩大数组的大小。expandTo方法及相关方法(checkInvariants)在文章最后给出。

get方法:
    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);
    }

当访问的wordIndex大于实际长度时,会直接返回FALSE,否则会根据对应位的0 、1返回。


------------------------------------------------------
    /**
     * Ensures that the BitSet can hold enough words.
     * @param wordsRequired the minimum acceptable number of words.
     */
    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;
        }
    }

    /**
     * Ensures that the BitSet can accommodate a given wordIndex,
     * temporarily violating the invariants.  The caller must
     * restore the invariants before returning to the user,
     * possibly using recalculateWordsInUse().
     * @param wordIndex the index to be accommodated.
     */
    private void expandTo(int wordIndex) {
        int wordsRequired = wordIndex+1;
        if (wordsInUse < wordsRequired) {
            ensureCapacity(wordsRequired);
            wordsInUse = wordsRequired;
        }
    }

    private void checkInvariants() {
        assert(wordsInUse == 0 || words[wordsInUse - 1] != 0);
        assert(wordsInUse >= 0 && wordsInUse <= words.length);
        assert(wordsInUse == words.length || words[wordsInUse] == 0);
    }










你可能感兴趣的:(java,源码,bitset)