目录

Bitset介绍

结构组成

构造函数

无参构造函数

有参构造函数

初始化函数

常用方法

【set】设置索引位有效

【get】获取索引位是否存在

【flip】索引翻转

【or】或运算

【xor】异或运算

【and】与运算

【andNot】非运算

【cardinality】计算有效索引位的数量

【clear】清空桶


Bitset介绍

Bitset位图,其中最核心的部分是words数组,也就是桶位,每个桶的存放类型为long类型

Bitset主要用作数字位的存储,计算方式是每个桶位的拼接态,示例

第一个桶位(长度为64的long类型二进制)

0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1000

第二个桶位(长度为64的long类型二进制)

0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1100

那么这个Bitset存储的值有3个数,并且每个数按位的角标确定数值大小,第一个桶仅存储了一个值为下标为3的位置,则存储的数为3,第二个桶存储了两个数,分别是下标为3和4的位置,但是由于拼接态,第二桶的下标要加上前面所有桶的长度之和,则第二个桶存储的数为(64+2)66和(64+3)67。*(两个桶转成十进制的数值分别是8和12,但是Bitset不是这种计算模式)

已知long类型占用内存空间是64个比特位,也就是8字节(1字节 = 8比特,因此一个long类型的数占用64字节),存储量如下:

1G的内存空间,则有 8bit * 1024 * 1024 * 1024 = 8.58* bit

由于Bitset是按long长度计数,长度多长就有多少个数,因此1G的内存可以存储85亿左右的数字,是大数据处理统计的神器。

结构组成

  • ADDRESS_BITS_PER_WORD: 固定值为6,主要用作位运算的常量

x >> 6 相当于除以64,由于long类型是64,这个运算是为了定位数组位置

x << 6 相当于乘以64,由于long类型是64,这个运算是为了定位数组位置

  • BITS_PER_WORD:值为64,作为长度计算值
  • BIT_INDEX_MASK:索引掩码
  • words:核心变量,数组桶位
  • wordsInUse:记录数组桶位的有效长度
  • sizeIsSticky:保护初始化构造方法时自定义的数组长度
    private final static int ADDRESS_BITS_PER_WORD = 6;private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1;private static final long WORD_MASK = 0xffffffffffffffffL;private long[] words;private transient int wordsInUse = 0;private transient boolean sizeIsSticky = false;

构造函数

无参构造函数

initWords方法初始化了数组,由于使用了默认是数组长度,则长度保护关闭。

    public BitSet() {// 初始化数组initWords(BITS_PER_WORD);// 长度保护关闭sizeIsSticky = false;}

有参构造函数

  1. 自定义nbits,是按比特位的多少进行初始化,调用initWords中的wordIndex方法中,将nbits作了位运算 nbits >> 64,目的是为了知道多少个long加起来的比特位可以容纳下nbits,使初始化数组的长度不会冗余。
  2. 直接传入的words数组,则按自定义的数组长度为准
    public BitSet(int nbits) {// 自定义长度小于0则抛出异常if (nbits < 0)throw new NegativeArraySizeException("nbits < 0: " + nbits);// 根据自定义长度初始化数组initWords(nbits);// 自定义长度保护开启sizeIsSticky = true;}private BitSet(long[] words) {// 直接使用传入的桶位数组this.words = words;// 再用数组长度赋值this.wordsInUse = words.length;checkInvariants();}

初始化函数

initWords:初始化数组

wordIndex:根据比特位索引,除以64(bitIndex >> 6),得到第n个桶位。

    private void initWords(int nbits) {words = new long[wordIndex(nbits-1) + 1];}private static int wordIndex(int bitIndex) {// 计算数组的索引位置return bitIndex >> ADDRESS_BITS_PER_WORD;}

常用方法

【set】设置索引位有效

执行逻辑:

  1. 检验索引有效范围
  2. 计算比特位索引对应的桶位角标
  3. expand重置桶位使用长度
  4. set的索引位通过 ‘或运算’ 置为1,1即为有效
    public void set(int bitIndex) {// 1.索引检验if (bitIndex < 0)throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);// 2.计算比特位索引对应的桶位int wordIndex = wordIndex(bitIndex);// 3.重置桶位使用长度expandTo(wordIndex);// 4.将set的索引位通过‘或运算’置为1words[wordIndex] |= (1L << bitIndex);checkInvariants();}private void expandTo(int wordIndex) {// 由于初始化函数时,也是根据wordIndex计算结果+1,故此处也要+1int wordsRequired = wordIndex+1;if (wordsInUse < wordsRequired) {ensureCapacity(wordsRequired);wordsInUse = wordsRequired;}}

【get】获取索引位是否存在

执行逻辑:

  1. 检验索引范围
  2. 使用的桶位长度检验
  3. 计算get的索引位置在哪个桶上
  4. 桶位未发生异常的情况下(防止并发),返回对应索引是否为1,为1返回true
    public boolean get(int bitIndex) {// 1.检验索引范围if (bitIndex < 0)throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);// 2.使用的桶位长度检验checkInvariants();// 3.计算get的索引位置在哪个桶上int wordIndex = wordIndex(bitIndex);// 4.桶位未发生异常的情况下(防止并发),返回对应索引是否为1,为1返回truereturn (wordIndex < wordsInUse)&& ((words[wordIndex] & (1L << bitIndex)) != 0);}

【flip】索引翻转

异或的作用:相同为0,不同为1,因此flip方法实质上是对撞抵消

场景:计算商品是否发货,含重复id的商品,通过flip令id抵消,表示商品已发出

执行逻辑:

  1. 检验索引范围
  2. 计算flip的索引位置在哪个桶上
  3. 重置桶位使用长度
  4. flip的索引位通过 ‘异或’ 进行翻转
  5. 重新计算桶位有效长度
    public void flip(int bitIndex) {// 1.检验索引范围if (bitIndex < 0)throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);// 2.计算flip的索引位置在哪个桶上int wordIndex = wordIndex(bitIndex);// 3.重置桶位使用长度expandTo(wordIndex);// 4.将flip的索引位通过 ‘异或’ 进行翻转words[wordIndex] ^= (1L << bitIndex);// 5.重新计算桶位有效长度recalculateWordsInUse();checkInvariants();}

【or】或运算

执行逻辑:

  1. 自身相计算,则结果不有变更,终止执行
  2. 使用两个Bitset公共桶位
  3. 当前Bitset桶位长度若小于传入的Bitset的桶位长度,则将当前Bitset桶位长度上升
  4. 对每个桶都进行 ‘或运算’
  5. 若公共桶位长度小于传入的Bitset的有效桶位长度,则将传入的Bitset的增量桶赋值给当前Bitset对应桶位
    public void or(BitSet set) {// 1.自身相计算,则结果不有变更,终止执行if (this == set)return;// 2.使用两个Bitset公共桶位长度int wordsInCommon = Math.min(wordsInUse, set.wordsInUse);// 3.当前Bitset桶位长度若小于传入的Bitset的桶位长度,则将当前Bitset桶位长度上升if (wordsInUse < set.wordsInUse) {ensureCapacity(set.wordsInUse);wordsInUse = set.wordsInUse;}// 4.对每个桶都进行 ‘或运算’for (int i = 0; i < wordsInCommon; i++)words[i] |= set.words[i];// 5.若公共桶位长度小于传入的Bitset的有效桶位长度,则将传入的Bitset的增量桶赋值给当前Bitset对应桶位if (wordsInCommon < set.wordsInUse)System.arraycopy(set.words, wordsInCommon,words, wordsInCommon,wordsInUse - wordsInCommon);// recalculateWordsInUse() is unnecessarycheckInvariants();}

【xor】异或运算

执行逻辑:

  1. 使用两个Bitset公共桶位
  2. 当前Bitset桶位长度若小于传入的Bitset的桶位长度,则将当前Bitset桶位长度上升
  3. 对每个桶都进行 ‘异或运算’
  4. 若公共桶位长度小于传入的Bitset的有效桶位长度,则将传入的Bitset的增量桶赋值给当前Bitset对应桶位
  5. 重新计算桶位有效长度
    public void xor(BitSet set) {// 1.使用两个Bitset公共桶位int wordsInCommon = Math.min(wordsInUse, set.wordsInUse);// 2.当前Bitset桶位长度若小于传入的Bitset的桶位长度,则将当前Bitset桶位长度上升if (wordsInUse < set.wordsInUse) {ensureCapacity(set.wordsInUse);wordsInUse = set.wordsInUse;}// 3.对每个桶都进行 ‘异或运算’for (int i = 0; i < wordsInCommon; i++)words[i] ^= set.words[i];// 4.若公共桶位长度小于传入的Bitset的有效桶位长度,则将传入的Bitset的增量桶赋值给当前Bitset对应桶位if (wordsInCommon < set.wordsInUse)System.arraycopy(set.words, wordsInCommon,words, wordsInCommon,set.wordsInUse - wordsInCommon);// 5.重新计算桶位有效长度recalculateWordsInUse();checkInvariants();}

【and】与运算

执行逻辑:

  1. 自身相计算,则结果不有变更,终止执行
  2. 若当前Bitset的有效桶位多于传入的Bitset的有效桶位数量,则将当前Bitset的多余桶位全部归0,因为1与0作 ‘与运算’ 结果为0,因此多余桶部分计算的结果也为0,免去位运算过程,直接归0,提效
  3. 对有效桶进行 ‘与运算’
  4. 重新计算桶位有效长度
    public void and(BitSet set) {// 1.自身相计算,则结果不有变更,终止执行if (this == set)return;// 2.若当前Bitset的有效桶位多于传入的Bitset的有效桶位数量,则将当前Bitset的多余桶位全部归0while (wordsInUse > set.wordsInUse)words[--wordsInUse] = 0;// 3.对有效桶进行 ‘与运算’for (int i = 0; i < wordsInUse; i++)words[i] &= set.words[i];// 4.重新计算桶位有效长度recalculateWordsInUse();checkInvariants();}

【andNot】非运算

执行逻辑:

  1. 循环的最大次数取两个Bitset的公共有效桶位
  2. 对有效桶进行 当前桶 ‘与运算’ 传入的桶(传入的桶先进行 ‘非运算’ 取反)
    public void andNot(BitSet set) {// Perform logical (a & !b) on words in common// 1.循环的最大次数取两个Bitset的公共有效桶位// 2.对有效桶进行 当前桶 ‘与运算’ 传入的桶(传入的桶先进行 ‘非运算’ 取反)for (int i = Math.min(wordsInUse, set.wordsInUse) - 1; i >= 0; i--)words[i] &= ~set.words[i];recalculateWordsInUse();checkInvariants();}

【cardinality】计算有效索引位的数量

执行逻辑:

  1. 循环计算每个桶内的long类型的数的比特位为1的数量,计算所有桶的所有有效位数量
    public int cardinality() {int sum = 0;// 1.循环计算每个桶内的long类型的数的比特位为1的数量,计算所有桶的所有有效位数量for (int i = 0; i < wordsInUse; i++)sum += Long.bitCount(words[i]);return sum;}

【clear】清空桶

执行逻辑:

  1. 循环便利有效桶位,并将每个桶置为0,同时有效桶位的计量单位wordsInUse也置为0
    public void clear() {// 1.循环便利有效桶位,并将每个桶置为0,同时有效桶位的计量单位wordsInUse也置为0while (wordsInUse > 0)words[--wordsInUse] = 0;}

Bitset 源码解析相关推荐

  1. 死磕Java集合之BitSet源码分析(JDK18)

    死磕Java集合之BitSet源码分析(JDK18) 文章目录 死磕Java集合之BitSet源码分析(JDK18) 简介 继承体系 存储结构 源码解析 属性 构造方法 set(int bitInde ...

  2. 谷歌BERT预训练源码解析(二):模型构建

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...

  3. 谷歌BERT预训练源码解析(三):训练过程

    目录 前言 源码解析 主函数 自定义模型 遮蔽词预测 下一句预测 规范化数据集 前言 本部分介绍BERT训练过程,BERT模型训练过程是在自己的TPU上进行的,这部分我没做过研究所以不做深入探讨.BE ...

  4. 谷歌BERT预训练源码解析(一):训练数据生成

    目录 预训练源码结构简介 输入输出 源码解析 参数 主函数 创建训练实例 下一句预测&实例生成 随机遮蔽 输出 结果一览 预训练源码结构简介 关于BERT,简单来说,它是一个基于Transfo ...

  5. Gin源码解析和例子——中间件(middleware)

    在<Gin源码解析和例子--路由>一文中,我们已经初识中间件.本文将继续探讨这个技术.(转载请指明出于breaksoftware的csdn博客) Gin的中间件,本质是一个匿名回调函数.这 ...

  6. Colly源码解析——结合例子分析底层实现

    通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...

  7. libev源码解析——定时器监视器和组织形式

    我们先看下定时器监视器的数据结构.(转载请指明出于breaksoftware的csdn博客) /* invoked after a specific time, repeatable (based o ...

  8. libev源码解析——定时器原理

    本文将回答<libev源码解析--I/O模型>中抛出的两个问题.(转载请指明出于breaksoftware的csdn博客) 对于问题1:为什么backend_poll函数需要指定超时?我们 ...

  9. libev源码解析——I/O模型

    在<libev源码解析--总览>一文中,我们介绍过,libev是一个基于事件的循环库.本文将介绍其和事件及循环之间的关系.(转载请指明出于breaksoftware的csdn博客) 目前i ...

  10. libev源码解析——调度策略

    在<libev源码解析--监视器(watcher)结构和组织形式>中介绍过,监视器分为[2,-2]区间5个等级的优先级.等级为2的监视器最高优,然后依次递减.不区分监视器类型和关联的文件描 ...

最新文章

  1. Oracle开发常用函数与存储过程
  2. arduino 大气气压模块 BOSCH BMP085
  3. 【发布】温度监测报警器v1.1a内测版!
  4. 论“前置测试模型”-1 概念篇
  5. 只需一行代码,完美呈现Markdown格式,写作展示两不误
  6. 2018-2019-2 20175308实验一 《Java开发环境的熟悉》实验报告
  7. 手写一个简化版Tomcat
  8. 滴滴章文嵩:一个人的 20 年开源热情和国内互联网开源运动
  9. 多线程之 interrupt,interrupted,isInterrupted 方法区别
  10. 锁屏界面显示某些设置已隐藏_一般人都不知道,iPhone 隐藏的功能,让你的苹果手机变得贼好用...
  11. android wifi驱动详解,Android wifi驱动的移植 realtek 8188
  12. 网络流——基础,Dinic和Sap(Gap优化)算法
  13. 纯CSS实现播放暂停按钮变形动画
  14. 基于mongodb的标签系统设计
  15. 7月16日周二晚上,陈勇,【敏捷网络课堂第六期】【免费】敏捷开发早期估算
  16. 随机信号分析笔记03:全概率公式和贝叶斯公式
  17. 韦东山 嵌入式Linux应用开发基础知识 下【串口 IIC SPI
  18. 用APPLOC挂载的程序如何拷贝剪贴板
  19. 【Unity】在Unity中实时显示北京时间
  20. FFMPEG学习----打印视频信息

热门文章

  1. java过滤器是用来干什么的_java过滤器有什么作用
  2. ps——投影字体效果
  3. Fluent 全流程求解多孔介质算例
  4. Microsoft C++ 异常: std::length_error,位于内存位置 0x000000AF9B7AF810 处
  5. win10/win11无损扩大C盘空间,跨盘合并C、E盘
  6. 5G时代芯片之王——射频芯片
  7. Spring子项目了解
  8. PHP 网页支付支付宝支付接口对接
  9. preg_match用法
  10. 读笔:《重来也不会好过现在》