JDK源码 - BitSet的实现

java.util.BitSet是个很有趣的类,了解其内部实现对正确的使用非常重要。 

对象构造: 

Java代码   收藏代码
  1. private final static int ADDRESS_BITS_PER_WORD = 6;  
  2. private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;  
  3. private long[] words;  
  4.   
  5. private static int wordIndex(int bitIndex) {  
  6.     return bitIndex >> ADDRESS_BITS_PER_WORD;  
  7. }  
  8.   
  9. private void initWords(int nbits) {  
  10.     words = new long[wordIndex(nbits-1) + 1];  
  11. }  
  12.   
  13. public BitSet() {  
  14.     initWords(BITS_PER_WORD);  
  15.     ...  
  16. }  
  17.   
  18. public BitSet(int nbits) {  
  19.     ...  
  20.     initWords(nbits);  
  21.     ...  
  22. }  



从贴出来的代码可以看出,long[] words这个数组是BitSet内部的关键实现,如果用户在构造函数中输入一个nbits变量,initWords方法会把这个数减1再右移6位加1,按照这个长度产生words数组的长度。 
如果是输入的28,那么words的长度是1, 
如果是输入的2^6       = 64,那么words的长度是1, 
如果是输入的2^6+1     = 65,那么words的长度是2, 
如果是输入的(2^6)*2   = 128,那么words的长度是2, 
如果是输入的(2^6)*2+1 = 129,那么words的长度是3, 
如果是输入的(2^6)*3   = 192,那么words的长度是3, 
如果是输入的(2^6)*3+1 = 193,那么words的长度是4, 
... 

到这里已经很清楚了,BitSet用long类型表示“位图”,因为一个long是64bit,所以每个long表示64个数据,也就是说:数组中words中的第一个long表示0~63,第二个long表示64~127,第三个long表示128~191 ... 

再看看get函数,检测某个数是否被置位: 

Java代码   收藏代码
  1. public boolean get(int bitIndex) {  
  2.     if (bitIndex < 0)  
  3.         throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);  
  4. ...  
  5.     int wordIndex = wordIndex(bitIndex);  
  6.     return (wordIndex < wordsInUse)  
  7.         && ((words[wordIndex] & (1L << bitIndex)) != 0);  
  8. }  


说明: 
- wordsInUse变量主要用来控制long的容量,当set的数值过大时,BitSet类可以扩充words数组的长度,这一点和很多集合类(例如ArrayList,HashMap)是相似的 
- 下面的语句值得注意: 
1L << bitIndex 
一般看到这条语句,会认为bitIndex如果超过64位,高位会溢出并得到返回0,事实上这个1会重新循环到低位,也就是说: 
1L << 64 返回为1。 
术语上这叫循环左移,经过检测java同样支持循环右移,运行下面的测试: 

Java代码   收藏代码
  1. for (int i = 0;i < 70 ;i++)  
  2.     System.out.println(i + " =" + (1 >> i));  


在i为0,32,64时,(1 >> i)重新为1,搞位运算编程的需要注意这个陷阱。 

注: 经过仔细考虑和试验,这里不是循环左移和循环右移,是一种比较“奇怪”的实现,回头写写这个问题。 

BitSet类的一个缺陷: 
size方法属于类的内部实现细节,导出成公有方法会让不了解实现细节的开发人员很迷惑。 

Java代码   收藏代码
  1. public int size() {  
  2.     return words.length * BITS_PER_WORD;  
  3. }  



例如: 开发人员可能通过bitset.set(100)设置位,然后调用bitset.size(),如果不了解细节,很难理解为什么结果为128。 

另外,如果实现位域(bit fields),应该考虑使用EnumSet,这一点可以参考Effective Java。

转载于:https://www.cnblogs.com/litaobupt/articles/3100070.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值