在java中,一个int类型占4个字节,也就是32bit,我们用一个int数组来表示时 new int[32],总计占用内存大概32*32bit,如果说我们存放的海量数据,亿万级非常大,那么这些基本数据类型都不够用的,则可以用int字节码的每一位表示一个数字,比如int类型32位,可以存放0-31共32个数值,那么32个数字只需要一个int类型所占内存空间大小就够了,这样在大数据量的情况下会节省很多内存。

一、位图作用

存放海量数据,节省存储空间有明显优势

二、位图实现

一、思路分析

位图的定义我们大概了解,就是通过定义一个整形数值后,将原本只表示1个数值的情况下,扩大了可以存放几十个数及以上的结构,比如我们要存放0-63的数,共64个数,那么我们就定义一个long型变量,有8个字节,64位,二进制就是64位,那么每一位从左到右就可以表示0,1,2..,63,如果有数则可以赋值1 没有则表示0 比如保存1,那么就是第二位赋值1 ,以此类推

下面我们演示下,传递一个数值,保存到一个long[] arr数组中,简单了解下:

arr[0]存放的值: 0 - 63          

arr[1]存放的值: 64 - 127

arr[2]存放的值: 128 - 191

.....

num =4  存放的位置就是 arr[0] 第一个元素 因为 num /64 = 0 ,元素二进制 00..10000 第五位赋值1

二、代码演示

package class05;import java.util.HashSet;public class Code02_BitMap2 {// 这个类的实现是正确的public static class BitMap {//定义long数组,一个元素可以存放64个数,因为long类型是8字节 64位private long[] bits;//定义数组长度,需要先给需要存储的数集的最大值 (max+64/64) 表示需要多少个,如 max是63 那个得 1 只需一个元素就可以//如果是64 得 2,就需要2个元素 因为一个Long64位 存放0-63的数值, 接着是64-127public BitMap(int max) { bits = new long[(max + 64) >> 6];}/*保存数值1.num>>6 表示先将目标数值除以64 得到该数值是位于bits数组的第几个,比如63 / 64 = 0 在bit[0] 64/64=1 bit[1]..2.num & 63 表示num % 64 , 即看在该元素的第几位, 2的6次方=64 第7位1000000 所以可以知道求模后肯定余数是1-6位的数,那么与运算 111111 即 63 就能表示1-6的值,也就是余数,也就得到是位于该元素中的第几位了 比如余数0 那么就是第一位 ,1 就是第二位...  确定了这个余数 即第几位,那么我们就用 1l << 余数(1必须要为long型 1L 取值长度才不会越界,int是32位,所以右移63肯定位数不够)  将1右移余数位,比如余数1 那么就1L右移一位,表示在这个位置标1了。3.前面得到了对应在第几个元素中的第几位,最后就是需要在这个元素的这个位置赋值1. 那么就是可以将元素位置bits[num >>6] =bits[num >>6] | (1L << (num & 64)  即或运算 前面得到的具体位置,有1 则表示1 所以就给元素的对于位置赋值1了*/public void add(int num) {bits[num >> 6] |= (1L << (num & 63));}/*删除数值, 就是将对应的元素中的第几位 将其1改成01.bits[num >> 6] 元素所在数组的元素, 1L << (num & 63) 64位元素中的第几位 表示存放着num2.对1L << (num & 63) 取反, 就得到一个 1111...01111 即将存放位赋值0 其他为13.将取反后的数 与bits[num >> 6] 所在元素就行 与运算, 此时其他位都位1 与完元素不变,而num所在位是0 与完则为0,则表示将数组删除*/public void delete(int num) {bits[num >> 6] &= ~(1L << (num & 63));}/*判断数值是否包含,那么就是判断 bits[num >> 6] 所在元素的(1L << (num & 63)) 所在位 进行与运算,(1L << (num & 63))所在位为1 其他位都为0 所以与运算后 如果为一 表示bits[num >> 6] 的所在位也是1 那么就返回true*/public boolean contains(int num) {return (bits[num >> 6] & (1L << (num & 63))) != 0;}}public static void main(String[] args) {System.out.println("测试开始!");int max = 10000;BitMap bitMap = new BitMap(max);HashSet<Integer> set = new HashSet<>();int testTime = 10000000;for (int i = 0; i < testTime; i++) {int num = (int) (Math.random() * (max + 1));double decide = Math.random();if (decide < 0.333) {bitMap.add(num);set.add(num);} else if (decide < 0.666) {bitMap.delete(num);set.remove(num);} else {if (bitMap.contains(num) != set.contains(num)) {System.out.println("Oops!");break;}}}for (int num = 0; num <= max; num++) {if (bitMap.contains(num) != set.contains(num)) {System.out.println("Oops!");}}System.out.println("测试结束!");}}

三、核心总结

  1. 定义数组长度,需要先给需要存储的数集的最大值 (max+64/64) 表示需要多少个,如 max是63 那个得 1 只需一个元素,如果是64 得 2,就需要2个元素 因为一个Long64位 存放0-63的数值, 接着是64-127
  2. num>>6 表示先将目标数值除以64 得到该数值是位于bits数组的第几个,比如63 / 64 = 0 在bit[0]  64/64=1 bit[1]
  3. num & 63 表示num % 64 , 即看在该元素的第几位, 2的6次方=64 第7位1000000 所以可以知道求模后肯定余数是1-6位的数,那么与运算 111111 即 63 就能表示1-6的值,也就是余数,也就得到是位于该元素中的第几位了 比如余数0 那么就是第一位 ,1 就是第二位... 
  4. 确定了这个余数 即第几位,那么我们就用 1l << 余数(1必须要为long型 1L 取值长度才不会越界,int是32位,所以右移63肯定位数不够)  将1右移余数位,比如余数1 那么就1L右移一位,表示在这个位置标1了
  5. 保存数值,将对应位赋值1:前面得到了对应在第几个元素中的第几位,最后就是需要在这个元素的这个位置赋值1. 那么就是可以将元素位置bits[num >>6] =bits[num >>6] | (1L << (num & 64)  即或运算 前面得到的具体位置,有1 则表示1 所以就给元素的对于位置赋值1了
  6. 删除数值,就是将对应的元素中的第几位 将其1改成0
    1.bits[num >> 6] 元素所在数组的元素, 1L << (num & 63) 64位元素中的第几位 表示存放着num
    2.对1L << (num & 63) 取反, 就得到一个 1111...01111 即将存放位赋值0 其他为1
    3.将取反后的数 与bits[num >> 6] 所在元素就行 与运算, 此时其他位都位1 与完元素不变,而num所在位是0 与完则为0,则表示将数组删除
  7. 判断数值是否包含,那么就是判断 bits[num >> 6] 所在元素的(1L << (num & 63)) 所在位 进行与运算,(1L << (num & 63))
    所在位为1 其他位都为0 所以与运算后 如果为一 表示bits[num >> 6] 的所在位也是1 那么就返回true

【算法数据结构初阶篇】:位图bitMap相关推荐

  1. 数据结构初阶最终章------>经典八大排序(C语言实现)

    前言:   正如标题所言,本篇博客是数据结构初阶的最终章节.但不是数据结构的最终章节!事实上,诸如AVL 树,红黑树这样高阶复杂的数据结构使用C语言非常麻烦,这些数据结构我会放在后续的C++的博客中去 ...

  2. 数据结构初阶(4)(OJ练习【判断链表中是否有环、返回链表入口点、删除链表中的所有重复出现的元素】、双向链表LinkedList【注意事项、构造方法、常用方法、模拟实现、遍历方法、顺序表和链表的区别)

    接上次博客:数据结构初阶(3)(链表:链表的基本概念.链表的类型.单向不带头非循环链表的实现.链表的相关OJ练习.链表的优缺点 )_di-Dora的博客-CSDN博客 目录 OJ练习 双向链表--Li ...

  3. 二叉树前中后序遍历+刷题【中】【数据结构/初阶/C语言实现】

    文章目录 1. 二叉树基础操作 1.1 二叉树遍历 1.1.1 前序遍历 前序遍历(Pre-Order Traversal) 1.1.2 中序遍历 中序遍历(In-Order Traversal) 1 ...

  4. 【数据结构初阶】链表(下)——带头双向循环链表的实现

    目录 带头双向循环链表的实现 1.带头双向循环链表的节点类型 2.创建带头双向循环链表的节点 3.向带头双向循环链表中插入数据 <3.1>从链表尾部插入数据 <3.2>从链表头 ...

  5. 【初阶篇】iVX成语填字游戏制作

    (一) 在iVX官网的Demo里看到了"你画我猜"."看图猜成语"等小游戏的Demo,自己也来了兴致,制作了一个很简易的"填成语"小游戏.效 ...

  6. 【数据结构初阶】八大排序算法+时空复杂度

    学会控制自己是人生的必修课 文章目录 一.插入排序 1.直接插入排序 2.希尔排序 二.选择排序 1.直接选择排序 2.堆排序(已经建好堆的基础之上) 三.交换排序(Swap) 1.冒泡排序(大学牲最 ...

  7. 数据结构初阶之二叉树——概念篇

    目录 一. 树的概念及结构 1. 树的概念 2. 树的相关概念 3. 树的表示 4. 实际运用 二. 二叉树概念及结构 1. 概念 2. 完全二叉树和满二叉树 3. 二叉树的性质 三. 练习 四. 练 ...

  8. 【数据结构初阶】第八篇——二叉树的链式结构(二叉树的前、中和后序遍历+层序遍历+链式结构的实现+相关简单的递归问题)

    ⭐️本篇博客我要来和大家一起聊一聊数据结构中的二叉树的链式结构的实现及相关的一些问题的介绍 ⭐️博客代码已上传至gitee:https://gitee.com/byte-binxin/data-str ...

  9. 【数据结构】单链表(增、删、查、改)的实现 [初阶篇_ 复习专用]

最新文章

  1. Ubuntu 安装 ffmpeg
  2. Android动画效果translate、scale、alpha、rotate详解
  3. CPU上跑到 100 fps 的高精度PyTorch人脸特征点检测库
  4. Python踩坑记录
  5. 程序员面试题精选100题(05)-查找最小的k个元素[算法]
  6. 【限时免费】LiveVideoStack Meet | 北京:卷时代,多媒体人 生存指北
  7. 推荐几个单细胞数据分享和展示平台 | 短视频演示
  8. Oracle学习 第18天 .NET连接Oracle
  9. HTML基本功之文档结构
  10. 139团队(大型研发团队,大型敏捷开发团队,大型团队结构,敏捷绩效管理)...
  11. CSS中文字体如宋体/微软雅黑的英文名称写法
  12. spss-多元线性回归分析
  13. [ github ] 10道题串起 Pandas 常用知识点
  14. 如何找回被删除的文件
  15. 计算机毕业设计Python+uniapp校园服务微信小程序(小程序+源码+LW)
  16. 前端需要知道的CSS函数大全
  17. Redis使用入门(二)【Windows下Python客户端redis-py使用】
  18. ANSYS常见术语对照
  19. PIC 1508 TIM1的定时器门控的理解
  20. 苹果a12_盘点那些你不知道的苹果a12和a10x的差距

热门文章

  1. 吐槽腾讯动漫会员产品
  2. 搞笑四格漫画 - 阿吉先生
  3. 9种网页动画常用实现方式总结
  4. SEOER该如何看待搜搜搜狗联合?
  5. 手把手教你做人脸搜索系统
  6. 2022年卡塔尔世界杯
  7. easyexcel导出和导入
  8. windows11 upgrade
  9. SSM-下(拉勾教育后台管理系统-前端)
  10. catalina 卡特琳娜