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. private static int wordIndex(int bitIndex) {
  5. return bitIndex >> ADDRESS_BITS_PER_WORD;
  6. }
  7. private void initWords(int nbits) {
  8. words = new long[wordIndex(nbits-1) + 1];
  9. }
  10. public BitSet() {
  11. initWords(BITS_PER_WORD);
  12. ...
  13. }
  14. public BitSet(int nbits) {
  15. ...
  16. initWords(nbits);
  17. ...
  18. }

从贴出来的代码可以看出,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

JDK源码 - BitSet的实现相关推荐

  1. java调用clang编译的so_写Java这么久,JDK源码编译过没?编译JDK源码踩坑纪实

    好奇害死羊 很多小伙伴们做Java开发,天天写Java代码,肯定离不开Java基础环境:JDK,毕竟我们写好的Java代码也是跑在JVM虚拟机上. 一般来说,我们学Java之前,第一步就是安装JDK环 ...

  2. Netty和JDK源码来看Netty的NIO和JDK的NIO有什么不同

    JDK底层提供了NIO实现,在Linux环境会调用内核epoll. 但是Netty通过JNI的方式提供了Native Socket Transport,为什么Netty要自己搞一套NIO呢? 这篇文章 ...

  3. 为什么jdk源码推荐ThreadLocal使用static

    ThreadLocal是线程私有变量,本身是解决多线程环境线程安全,可以说单线程实际上没必要使用. 既然多线程环境本身不使用static,那么又怎么会线程不安全.所以这个问题本身并不是问题,只是有人没 ...

  4. JDK源码研究Jstack,JMap,threaddump,dumpheap的原理

    JDK最新bug和任务领取:https://bugs.openjdk.java.net/projects/JDK/issues 参加OpenJDK社区:https://bugs.openjdk.jav ...

  5. 调试JDK源码-ConcurrentHashMap实现原理

    调试JDK源码-一步一步看HashMap怎么Hash和扩容 调试JDK源码-ConcurrentHashMap实现原理 调试JDK源码-HashSet实现原理 调试JDK源码-调试JDK源码-Hash ...

  6. 调试JDK源码-一步一步看HashMap怎么Hash和扩容

    调试JDK源码-一步一步看HashMap怎么Hash和扩容 调试JDK源码-ConcurrentHashMap实现原理 调试JDK源码-HashSet实现原理 调试JDK源码-调试JDK源码-Hash ...

  7. 调试JDK源码-Hashtable实现原理以及线程安全的原因

    调试JDK源码-一步一步看HashMap怎么Hash和扩容 调试JDK源码-ConcurrentHashMap实现原理 调试JDK源码-HashSet实现原理 调试JDK源码-调试JDK源码-Hash ...

  8. 调试JDK源码-HashSet实现原理

    调试JDK源码-一步一步看HashMap怎么Hash和扩容 调试JDK源码-ConcurrentHashMap实现原理 调试JDK源码-HashSet实现原理 调试JDK源码-调试JDK源码-Hash ...

  9. Java是如何实现自己的SPI机制的? JDK源码(一)

    注:该源码分析对应JDK版本为1.8 1 引言 这是[源码笔记]的JDK源码解读的第一篇文章,本篇我们来探究Java的SPI机制的相关源码. 2 什么是SPI机制 那么,什么是SPI机制呢? SPI是 ...

最新文章

  1. openssl-1.0.1用mingw编译
  2. C++ 成员函数做友元
  3. leetcode 668. Kth Smallest Number in Multiplication Table | 668. 乘法表中第k小的数(二分查找)
  4. Angular里的style property binding的一个例子
  5. Java GUI界面
  6. 【转】ASP.NET内幕 - IIS处理模型
  7. vue 组件第一次不渲染问题_vue使用组件不渲染 只有代码变了才渲染
  8. VsCode git报错 git add -A -- xxx is outside repository
  9. origin坐标轴在隐藏后如何显示
  10. 小白也能看懂的 Java 异常处理
  11. centos php管理面板,CentOS Web Panle控制面板PHP Version Switcher
  12. centos中service命令与/etc/init.d的关系以及centos7的变化
  13. 国家java认证考试报名入口,值得一读!
  14. 医用计算机考试题目,2015年全国计算机应医用能力考试辅导资料.doc
  15. win10系统ltsc和服务器版哪个好,win10哪个版本最稳定流畅
  16. 实战PHP皮皮虾去水印解析接口
  17. 目录扫描暴力破解网站管理员密码
  18. 一个HTTP打趴80%面试者
  19. 运动学逆解(四足机器狗)
  20. mysql数据库存储经度纬度

热门文章

  1. dataframe去重复 python_python – 在DataFrame中组合重复的列
  2. mysql dql_Mysql中的DQL查询语句
  3. Spring MVC 中 HandlerInterceptorAdapter的使用
  4. jquery实现页面提示,数据正在加载中。(
  5. ajax对日期处理,AJAX获取服务器当前时间及时间格式输出处理
  6. 指针、引用以及const限定符、constexpr限定符
  7. Pandas数据排序——【按索引排序sort_index()方法、按值排序sort_value()方法】
  8. Python 图片转简单字符画
  9. C++:11---友元函数、友元类
  10. 我对STL的一些看法(一)初步认识STL