BitMap源码解析

news2024/9/30 19:32:17

文章目录

  • 前言
  • 数据结构
    • 添加与删除操作
  • JDK中BitSet源码解析
    • 重要成员属性
    • 初始化
    • 添加数据
    • 清除数据
    • 获取数据
    • size和length方法
    • 集合操作:与、或、异或
    • 优缺点

前言

为什么称为bitmap?
bitmap不仅仅存储介质以及数据结构不同于hashmap,存储的key和value也不同。

bitmap的key是元素的index,value只有0或者1(具体结构见下文)。

数据结构

Bit-map的基本思想就是用一个bit位来标记某个元素对应的Value,而Key即是该元素。由于采用了Bit为单位来存储数据,因此可以很大程度上节省存储空间。

举例:
bitmap
key-value: bitmap[1] = 1、bitmap[2]=0

添加与删除操作

添加:使用1和key所在位的value进行 |(或)

删除:使用1和key所在位的value进行 &(与)

JDK中BitSet源码解析

位于java.util包中

重要成员属性

/*
 * BitSets are packed into arrays of "words."  Currently a word is
 * a long, which consists of 64 bits, requiring 6 address bits.
 * The choice of word size is determined purely by performance concerns.
 * 采用long作为载体,long有8个byte,所以有一个long有64个bit,64这个数字需要6个bit承载
 */
private final static int ADDRESS_BITS_PER_WORD = 6;
// 每一个words里面的元素占有64位
private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1;
/* Used to shift left or right for a partial word mask */
private static final long WORD_MASK = 0xffffffffffffffffL;
/**
 * @serialField bits long[]
 *
 * The bits in this BitSet.  The ith bit is stored in bits[i/64] at
 * bit position i % 64 (where bit position 0 refers to the least
 * significant bit and 63 refers to the most significant bit).
 */
private static final ObjectStreamField[] serialPersistentFields = {
    new ObjectStreamField("bits", long[].class),
};
/**
 * The internal field corresponding to the serialField "bits".
 * bitset的数据载体
 */
private long[] words;
/**
 * The number of words in the logical size of this BitSet.
 * 表示数组中最多使用的元素个数,也就是最后一个不为 0 的元素的索引加 1;比如[0,4,0,0],数组长度为 4,但是最后一个不为 0 的元素是 1,所以 wordsInUse = 2
 */
private transient int wordsInUse = 0;

初始化

创建一个 BitSet 对象时,默认 words 的长度为 1,并且 words[0] = 0。当然也可以用户给定一个具体的容量大小,如下代码:

/**
* BitSet.class
* 创建一个能存储给定数据索引的 BitSet
*/
public BitSet(int nbits) {
    // 参数合法性判断
    if (nbits < 0)
        throw new NegativeArraySizeException("nbits < 0: " + nbits);
    // 调用 initWords 方法初始化
    initWords(nbits);
    sizeIsSticky = true;
}

private void initWords(int nbits) {
    words = new long[wordIndex(nbits-1) + 1];
}
// 得到 bitIndex 对应的 words 下标
private static int wordIndex(int bitIndex) {
    return bitIndex >> ADDRESS_BITS_PER_WORD;
}

添加数据

public void set(int bitIndex) {
    // 参数合法性检验
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
    // 得到对应的数组下标
    int wordIndex = wordIndex(bitIndex);
    // 是否要扩容
    expandTo(wordIndex);
    // 修改数据
    words[wordIndex] |= (1L << bitIndex); 
    // 参数检查
    checkInvariants();
}
private void expandTo(int wordIndex) {
    int wordsRequired = wordIndex+1;
    if (wordsInUse < wordsRequired) {
      	// 扩容
        ensureCapacity(wordsRequired);
        wordsInUse = wordsRequired;
    }
}
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;
        }
    }

注意这里的set(bitIndex)是让二进制的位置为1,并不是让words数组的某一index为1.
扩容的逻辑是:如果需要的长度大于数组的两倍,则扩容到需要的长度。否则,扩容位数组的两倍。

清除数据

public void clear(int bitIndex) {
    //...
    int wordIndex = wordIndex(bitIndex);
    // 如果 wordIndex >= wordsInUse,说明该索引要么不存在,要么一定是 0 ,直接返回即可
    if (wordIndex >= wordsInUse)
        return;
    words[wordIndex] &= ~(1L << bitIndex);
    recalculateWordsInUse();
    //...
}
// 修改完可能会引起 wordsInUse 的变化,所以还要调用 recalculateWordsInUse() 重新计算 wordsInUse:从后往前遍历直到遇到 words[i] != 0,修改 wordsInUse = i+1。
private void recalculateWordsInUse() {
    int i;
    for (i = wordsInUse-1; i >= 0; i--)
        if (words[i] != 0)
            break;

    wordsInUse = i+1; // The new logical size
}

获取数据

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

size和length方法

/**
 * Returns the number of bits of space actually in use by this
 * {@code BitSet} to represent bit values.
 * The maximum element in the set is the size - 1st element.
 *
 * @return the number of bits currently in this bit set
 */
public int size() {
    return words.length * BITS_PER_WORD;
}

/**
 * Returns the "logical size" of this {@code BitSet}: the index of
 * the highest set bit in the {@code BitSet} plus one. Returns zero
 * if the {@code BitSet} contains no set bits.
 * 最高非0位+1
 *
 * @return the logical size of this {@code BitSet}
 * @since  1.2
 */
public int length() {
    if (wordsInUse == 0)
        return 0;
    return BITS_PER_WORD * (wordsInUse - 1) +
        (BITS_PER_WORD - Long.numberOfLeadingZeros(words[wordsInUse - 1]));
}
  • size方法:words数组的长度 * 64(每个long的长度)
  • lenght方法:最高位的1所在位置+ 1
    示例:
    示例

集合操作:与、或、异或

集合操作还是很常用的,具体不作说明了,自行去看源码。

优缺点

优点:可以大幅减少数据存储空间,适合稠密的数据场景
缺点:当数据稀散的时候,会浪费空间(例如存储1,1000000)

本文就到这里,为了解决普通bitmap的缺点,下一篇将介绍它的变体RoaringBitMap。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1378658.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【复现】Spider-Flow RCE漏洞(CVE-2024-0195)_16

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 Spider Flow 是一个高度灵活可配置的爬虫平台&#xff0c;用户无需编写代码&#xff0c;以流程图的方式&#xff0c;即可实现爬虫…

乱码问题汇总

写在前面 在工作中经常会碰到各种莫名其妙的乱码问题&#xff0c;但通过之前的学习&#xff1a;字符集&字符编码-CSDN博客 &#xff0c;可以知道乱码的根本原因就是使用和数据源编码不一样的编码解码导致。 如&#xff1a;BIG5解码GB2312编码内容&#xff0c;编解码不一致…

面试算法118:多余的边

题目 树可以看成无环的无向图。在一个包含n个节点&#xff08;节点标号为从1到n&#xff09;的树中添加一条边连接任意两个节点&#xff0c;这棵树就会变成一个有环的图。给定一个在树中添加了一条边的图&#xff0c;请找出这条多余的边&#xff08;用这条边连接的两个节点表示…

Unity中URP下实现深度贴花

文章目录 前言一、场景设置二、实现思路1、通过深度图求出像素所在视图空间的Z值2、通过模型面片的求出像素在观察空间下的坐标值3、结合两者求出 深度图中像素的 XYZ值4、再将此坐标转换到模型的本地空间&#xff0c;把XY作为UV来进行纹理采样 三、URP下实现1、通过深度图求出…

2024年最新软件测试面试题

Part1 1、你的测试职业发展是什么&#xff1f;【文末有面试文档免费领取】 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做…

Spark的内核调度

目录 概述 RDD的依赖 DAG和Stage DAG执行流程图形成和Stage划分 Stage内部流程 Spark Shuffle Spark中shuffle的发展历程 优化前的Hash shuffle 经过优化后的Hash shuffle Sort shuffle Sort shuffle的普通机制 Job调度流程 Spark RDD并行度 概述 Spark内核调度任务: 1…

力扣67. 二进制求和算法

一、【写在前面】 这道题需要&#xff0c;给你两个字符串比如 a "1010", b "1011"答案是&#xff1a;"10101" 然后需要你给出计算结果&#xff0c;那么我们很容易想到两种做法 1. 调库做法&#xff1a;直接转化为整数&#xff0c;然后用内…

python统计分析——小提琴图(sns.violinplot)

参考资料&#xff1a;用python动手学统计学&#xff0c;帮助文档 使用seaborn.violinplot()函数绘制箱线图 sns.violinplot()的做出来的小提琴图比plt.violinplot()更像小提琴。 import numpy as np import pandas as pd from matplotlib import pyplot as plt import seabo…

【自控实验】4. 数字仿真实验

本科课程实验报告&#xff0c;有太多公式和图片了&#xff0c;干脆直接转成图片了 仅分享和记录&#xff0c;不保证全对 使用matlab中的simulink进行仿真 实验内容 线性连续控制系统的数字仿真 根据开环传递函数G(S)的不同&#xff0c;完成两个线性连续控制系统的仿真。 …

MySQL面试题 | 03.精选MySQL面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【linux】NIO中的FileChannel与mmap

FileChannel是Java NIO库中的一个类&#xff0c;用于对文件进行读写操作。它提供了一种高效的方式来读取、写入和操作文件。 使用FileChannel&#xff0c;你可以执行以下操作&#xff1a; 从文件读取数据到缓冲区&#xff08;Buffer&#xff09;&#xff1a;你可以使用FileCh…

QT 小组件 列表框以及微调框

.cpp文件 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);QListWidgetItem *pPhone new QListWidgetItem;pPhone->setText("西瓜");pPhone->…

SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测

SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测 目录 SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多…

eclipse ADT安装及abap cds模版创建

文章目录 1.前提2.安装3.创建cds模版 abap cds 常用语法 https://blog.csdn.net/weixin_49198221/article/details/135531478?spm1001.2014.3001.5501 1.前提 需要了解版本关系: **1.eclipse:**2023-06 (4.28), 2023-09 (4.29), 2023-12 (4.30) 2.Windows: ​ 1.Windows …

java多线程(并发)夯实之路-线程池深入浅出

线程池 Thread Pool&#xff1a;线程池&#xff0c;存放可以重复使用的线程&#xff08;消费者&#xff09; Blocking Queue&#xff1a;阻塞队列&#xff0c;存放等待执行的任务&#xff08;生产者&#xff09; poll方法&#xff08;有时限地获取任务&#xff09;相对take注…

【每日小bug】——mybatis-plus拼接sql空格报错,根据时间聚合查询

mybatis-plus拼接sql报错 复制报错sql语句到navicat,字段之间缺少空格&#xff0c;补上就可以了 聚合sql 根据时间 json接收JsonFormat(timezone "GMT8", pattern "yyyy-MM-dd")DateTimeFormat(pattern "yyyy-MM-dd")private Date startTim…

如何为数据保护加上“安全锁”?

伴随着数字经济的日趋活跃&#xff0c;数据安全和隐私保护成为了各国政府和企业都十分重视的问题&#xff0c;纷纷加强了数据安全防护。但实际上&#xff0c;近几年数据泄露问题接连不断&#xff0c;虽然没有造成严重的后果&#xff0c;但也足以证明目前数据安全防护的紧迫性。…

使用 Github、Hugo 搭建个人博客

Hugo 静态网站构建手册&#xff1a;https://jimmysong.io/hugo-handbook/ 关键字&#xff1a;开源 博客 框架 1、GitHub Pages 官网&#xff1a;https://pages.github.com/ 文档&#xff1a;https://docs.github.com/zh Github Pages 简介 Websites for you and your project…

京东年度数据报告-2023全年度游戏本十大热门品牌销量(销额)榜单

同笔记本市场类似&#xff0c;2023年度游戏本市场的整体销售也呈下滑态势。根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;京东平台上游戏本的年度销量累计超过350万&#xff0c;同比下滑约6%&#xff1b;销售额将近270亿&#xff0c;同比下滑约11%。 鲸参谋综合了京东…

2.idea查看不到git的提交文件

&#xff08;1&#xff09;查看日志 使用idea工具查看git提交日志&#xff0c;如下&#xff1a;项目名上右击&#xff0c;选择git->Show History &#xff08;2&#xff09;预期结果 &#xff08;3&#xff09;实际结果 只能看见此次提交的commit id&#xff0c;看不见所修…