接着上一篇Blog:一道面试题与Java位操作 和 BitSet 库的使用,分析下Java源码中BitSet类的源码。

位图(Bitmap),即位(Bit)的集合,是一种常用的数据结构,可用于记录大量的0-1状态,在很多地方都会用到,比如Linux内核(如inode,磁盘块)、Bloom Filter算法等,其优势是可以在一个非常高的空间利用率下保存大量0-1状态。在Java中,直接面向程序员的最小数据操作粒度是byte,并不提供能够直接操作某个bit的途径,但是程序员可以通过使用位运算符(& | ~ << >> 等等)自己封装位操作。如果不想自己动手,可以使用Java内置的BitSet类,其实现了位图数据结构并提供了一系列有用的接口。

java.util.BitSet这个类不大,代码不到1200行,理解起来也不困难,下面分析一下关键的几处代码。(注意下面的代码是基于Oracle jdk1.7.0_45,或者点击这里看源码):

1.一些属性

1 /*

2 * BitSets are packed into arrays of "words." Currently a word is3 * a long, which consists of 64 bits, requiring 6 address bits.4 * The choice of word size is determined purely by performance concerns.5 */

6 private final static int ADDRESS_BITS_PER_WORD = 6;7 private final static int BITS_PER_WORD = 1 <

10 /*Used to shift left or right for a partial word mask*/

11 private static final long WORD_MASK = 0xffffffffffffffffL;

其实注释已经写得很清楚,BitSet是用long[]来存储数据,一个long是64个bit,所以ADDRESS_BITS_PER_WORD就是6(2^6=64,即表示64个值需要6个地址线的意思)。BITS_PER_WORD是1算数左移6位,即1 × 2^6 = 64,意为一个“字”(long)包含64个bit。BIT_INDEX_MASK是63,即16进制的0x3f,可理解成低6位全为1。WORD_MASK,全1,用于掩码。

至于为什么选择long这种数据类型,注释的解析是基于性能的原因,现在64位CPU已经非常普及,可以一次把一个64bit长度的long放进寄存器作计算。

1 **

2 * The internal field corresponding to the serialField "bits".3 */

4 private long[] words;

属性words即为实际存储数据的地方.

2.一些公共函数

1 /**

2 * Given a bit index, return word index containing it.3 */

4 private static int wordIndex(intbitIndex) {5 return bitIndex >>ADDRESS_BITS_PER_WORD;6 }

这个静态函数在很多其它函数中会用到,用途是传入一个bit的索引值bitIndex,返回这个bit所在的那个long在long[]中的索引值。就是把bitIndex算数右移6位,也就是bitIndex除以64,因为long长度是64bit。比如第50个bit所对应的long就是50 / 64 = 0,即words中的第0个long。

3.构造函数

1 /**

2 * Creates a new bit set. All bits are initially {@codefalse}.3 */

4 publicBitSet() {5 initWords(BITS_PER_WORD);6 sizeIsSticky = false;7 }8

9 /**

10 * Creates a bit set whose initial size is large enough to explicitly11 * represent bits with indices in the range {@code0} through12 * {@codenbits-1}. All bits are initially {@codefalse}.13 *14 *@paramnbits the initial size of the bit set15 *@throwsNegativeArraySizeException if the specified initial size16 * is negative17 */

18 public BitSet(intnbits) {19 //nbits can't be negative; size 0 is OK

20 if (nbits < 0)21 throw new NegativeArraySizeException("nbits < 0: " +nbits);22

23 initWords(nbits);24 sizeIsSticky = true;25 }26

27 private void initWords(intnbits) {28 words = new long[wordIndex(nbits-1) + 1];29 }

如果用户调用默认构造函数,则会分配一个长度为64bit的BitSet,如果BitSet(int nbits),则会分配一个大于等于nbits并且是64的整数倍的BitSet,比如调用BitSet(100),则会分配长度为128的BitSet(即2个long)。

public static BitSet valueOf(long[] longs)public staticBitSet valueOf(LongBuffer lb)public static BitSet valueOf(byte[] bytes)public static BitSet valueOf(ByteBuffer bb)

BitSet也提供了一些静态函数让用户从一些已有的数据结构中直接构造BitSet。注意上面4个函数都是会把传入参数拷贝一个副本以供BitSet自己使用,所以并不会改变传入参数的数据。

4.动态扩展容量

上一篇Blog提到过,BitSet能够在一些操作(如Set())的时候,如果传入参数大于BitSet本身已有的长度,则它会自动扩展到所需长度。主要以来下面的函数:

1 /**

2 * Ensures that the BitSet can hold enough words.3 *@paramwordsRequired the minimum acceptable number of words.4 */

5 private void ensureCapacity(intwordsRequired) {6 if (words.length

8 int request = Math.max(2 *words.length, wordsRequired);9 words =Arrays.copyOf(words, request);10 sizeIsSticky = false;11 }12 }

这个函数的传入参数wordsRequired表示需要多少个“字”,它会与当前words的长度作比较,如果wordsRequired比较大的话,则会新建一个long[],长度取当前words长度的2倍与wordsRequired中较大的那个值,最后把当前words的内容拷贝到新long[]中,并把这个words指向这个新long[]。这就完成了动态扩容,跟ArrayList的实现方式非常类似,另一方面也看到这份代码不是线程安全的,多线程竞争下必须用户手动同步。

5.flip反转某一位

1 /**

2 * Sets the bit at the specified index to the complement of its3 * current value.4 *5 *@parambitIndex the index of the bit to flip6 *@throwsIndexOutOfBoundsException if the specified index is negative7 *@since1.48 */

9 public void flip(intbitIndex) {10 if (bitIndex < 0)11 throw new IndexOutOfBoundsException("bitIndex < 0: " +bitIndex);12

13 int wordIndex =wordIndex(bitIndex);14 expandTo(wordIndex);15

16 words[wordIndex] ^= (1L <

18 recalculateWordsInUse();19 checkInvariants();20 }

flip函数提供反转某一个位的功能。做法是先找到bitIndex所在的long,然后把这个long跟(1L << bitIndex)做“异或”操作(XOR)。注意bitIndex是可以大于63的,左移运算符(<

6.clear清除某一个位的值

1 /**

2 * Sets the bit specified by the index to {@codefalse}.3 *4 *@parambitIndex the index of the bit to be cleared5 *@throwsIndexOutOfBoundsException if the specified index is negative6 *@sinceJDK1.07 */

8 public void clear(intbitIndex) {9 if (bitIndex < 0)10 throw new IndexOutOfBoundsException("bitIndex < 0: " +bitIndex);11

12 int wordIndex =wordIndex(bitIndex);13 if (wordIndex >=wordsInUse)14 return;15

16 words[wordIndex] &= ~(1L <

18 recalculateWordsInUse();19 checkInvariants();20 }

其实也就是把某一个位设为0。过程与上面flip类似,但进行的位运算不一样,这里是把(1L << bitIndex)取反再跟words[wordIndex]进行“与”运算(AND)。原理其实很简单,布尔运算中一个值和1做AND运算,则其值不变;而如果和0做AND运算,则结果为0。比如:1100 & ~(0100) 等于 1100 & 1011 = 1000.

另外BitSet还提供了get, set接口、跟另一个BitSet对象做AND/OR/XOR运算的接口,这些都是用到位运算,比较好理解,不再赘述,请自行参考API.

java bitset javadoc_Java数据结构: java.util.BitSet源码学习相关推荐

  1. 蜘蛛纸牌java注释_GitHub - tangguangyao/SpiderSolitaire: 蜘蛛纸牌源码学习+注释

    SpiderSolitaire 蜘蛛纸牌源码学习+注释 源码分析思路: 首先,用了2个构造函数Spider和Poker Spider构造函数拥有以下方法: init:函数初始化 start:游戏开始 ...

  2. java.util.ServiceLoader源码分析

    java.util.ServiceLoader源码分析 回顾: ServiceLoader类的使用(具体参考博客http://blog.csdn.net/liangyihuai/article/det ...

  3. 【Java并发编程】16、ReentrantReadWriteLock源码分析

    一.前言 在分析了锁框架的其他类之后,下面进入锁框架中最后一个类ReentrantReadWriteLock的分析,它表示可重入读写锁,ReentrantReadWriteLock中包含了两种锁,读锁 ...

  4. Java多线程之JUC包:Semaphore源码学习笔记

    若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5625536.html Semaphore是JUC ...

  5. 基于JAVA体育用品购物系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署

    基于JAVA体育用品购物系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署 基于JAVA体育用品购物系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署 本源码技术栈: 项目架构 ...

  6. java计算机毕业设计网上快捷购物系统源码+系统+数据库+lw文档+mybatis+运行部署

    java计算机毕业设计网上快捷购物系统源码+系统+数据库+lw文档+mybatis+运行部署 java计算机毕业设计网上快捷购物系统源码+系统+数据库+lw文档+mybatis+运行部署 本源码技术栈 ...

  7. java计算机毕业设计医院人事档案管理系源码+系统+mysql数据库+lw文档

    java计算机毕业设计医院人事档案管理系源码+系统+mysql数据库+lw文档 java计算机毕业设计医院人事档案管理系源码+系统+mysql数据库+lw文档 本源码技术栈: 项目架构:B/S架构 开 ...

  8. 使用Java实现发送微信消息(附源码)_此程序在手再也不怕对象跟你闹了

    使用Java实现发送微信消息(附源码)_此程序在手再也不怕对象跟你闹了 此程序在手再也不怕女朋友跟你闹了!!!!自从有了女朋友比如:早安.晚安之类的问候语可不能断,但是也难免有时候会忘记那么该咋么办呢 ...

  9. JAVA毕业设计流浪狗领养系统计算机源码+lw文档+系统+调试部署+数据库

    JAVA毕业设计流浪狗领养系统计算机源码+lw文档+系统+调试部署+数据库 JAVA毕业设计流浪狗领养系统计算机源码+lw文档+系统+调试部署+数据库 本源码技术栈: 项目架构:B/S架构 开发语言: ...

  10. java语言金山打字_[Java教程]java实现 swing模仿金山打字 案例源码

    [Java教程]java实现 swing模仿金山打字 案例源码 0 2014-11-17 12:00:21 java实现 swing模仿金山打字 案例源码,更多Java技术就去Java教程网.http ...

最新文章

  1. [微信小程序]物流信息样式加动画效果(源代码附效果图)
  2. 基于 JWT + Refresh Token 的用户认证实践
  3. cmd运行Java中文乱码,无法加载主类Error: Could not find or load main class
  4. python 利用多进程实现文件的拷贝
  5. Linux 调优方案, 修改最大连接数(ulimit命令)
  6. mysql二级封锁协议_MySQL 行锁、两阶段锁协议、死锁以及死锁检测
  7. 使用 Postman 测试你的 API
  8. c语言找出递增子数组的长度,求给定数组的最长递增子序列(记录子序列的值)...
  9. 保存多序列tiff文件_干货技巧!如何使你保存的Phtoshop文件容量更小?
  10. 性能测试用例(转载)
  11. 阶段5 3.微服务项目【学成在线】_day01 搭建环境 CMS服务端开发_14-MongoDb入门-文档...
  12. python 书签内容整理
  13. 【教程】小米盒子4 刷机,无广告,总是无限重启,刷入2%报错的解决方法
  14. antdesign图片点击放大_vue图片点击放大功能
  15. openCV4+vs2019环境搭建
  16. 后端开发之如何写接口设计文档
  17. stata中计算公式命令_stata 计算命令:
  18. MT7921方案WIFI6无线网卡驱动编译方法
  19. sort函数和sorted函数的异同
  20. java比较两个对象_java判断两个对象是否相等的方法

热门文章

  1. 矢量网络分析仪测量总结
  2. 小程序获取openid时报40125
  3. 小米note2鸿蒙ROM,【ROM】小米note优化开发版MIUI9
  4. SCDM(3)建立几何
  5. 单元格下拉框实现复选框多选_将复选框链接到带有宏的单元格
  6. 港股历史行情数据 API 接口
  7. 现在公开一个DHT网络爬虫网络爬虫供大家一起交流
  8. 无法更新计算机的启动配置 注册表,win10改win7系统卡在安装程序正在更新注册表设置怎么办...
  9. 鼠标移上去变小手样式
  10. UG NX 12同步建模:调整面大小