java.util.BitSet使用与分析

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

我们这样都是用4个字节来存储了一个数据,在计算机里都是用二进制进行表示:
例如 5 :0000 0000 0000 0000 0000 0000 0000 0101
现在引入BitSet,所谓BitSet就是用一个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就进行输出,就完成了数据从小到大的排序。

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

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

源码
首先看构造方法:

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

其中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);  
}  

例子代码如下:

import java.util.BitSet;


public class TestBitSet { 
        public static void main(String[] args) {  
            int[] data={2,42,5,6,6,18,33,15,25,31,28,37};  
            //默认64,超过集合容量会扩大,从而影响效率,所以初始化时最好按照数组最大值进行初始化
            BitSet bs=new BitSet(64);  
            System.out.println("bs.size()--"+bs.size());  

            //默认BitSet是false
            for(int i=0;i<data.length;i++){
                bs.set(data[i], true) ;
            }
           for(int i=0;i<bs.size();i++){
               if(bs.get(i)){
                   System.out.println(i);
               }
           }
        }  
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值