所谓bitmap,就是用每一位来存放某种状态,适用于大规模数据,但数据状态又不是很多的情况。通常是用来判断某个数据存不存在的。

设计原则:

尽可能的最大化利用内存,极限挖掘、利用、发挥Java的性能。

设计思路:

使用long型数组来用作存储,

故位图Bitmap类的大小size使用long型(int型不够极限),所以理论上0<=size<=2^63-1

Java数组的长度最长为2^31-1(即int型字面量最大值),所以long类型的数组最多可以存储64*(2^31-1)个点(以只有两种状态的点来计算,即1bit表示一个点的状态),所以Bitmap的size不可能大于64*(2^31-1),故实际0<=size<=64*(2^31-1)

再继续分析位图中每个点不可能只有两种状态,所以需要设计出来的Bitmap可以自定义点的状态数量state,即1<state<=2^63-1(大于1是因为状态不可能只有1种,只有一种的话使用位图就失去了意义,小于等于2^63-1是因为2^63-1是long型字面量最大值,没有比该值还要大的整数值,符合宗旨:探索挖掘Java的极限使用);

此外自定义的点的状态数量需要使用多少位数来表示也是一个问题,自定义点的状态数量位数stateBitNum=(state的二进制表示的位数)

先做简单推导:

2种状态需要1bit,[0,1]

3~4种状态需要2bit,[00,01,10,11] 

5~8种状态需要3bit,[000,001,010,011,100,101,110,111]

9~16种状态需要4bit,[...,...]

这里给出计算公式:stateBitNum=64 - Long.numberOfLeadingZeros(state - 1)

Long.numberOfLeadingZeros(long i)方法是计算i的补码形式最高位的1的左面的0的数量,也就是补码中从左往右数0的个数,数到第一个非0的位置停止,0的数量即为该方法的返回值,举例:Long.numberOfLeadingZeros(2)=62,2的补码为10,故用64位补码(因为i是long型的)表示10前面还有62个0;因此64- Long.numberOfLeadingZeros(2)=2,而2种状态(state=2)其实只要用1位表示,所以state再传入该方法时要减一(因为0本身在计算机中就可以表示一种状态,比如8种状态可以用数字0~7来表示一样);

再继续深挖会发现64位其实可以表示2^64种状态,但是Java的最大字面量值为2^63-1,所以最大状态数量state=2^63-1种状态需要63位保存,一个long型足以,这里还有一个坑Long.numberOfLeadingZeros(0)=64,但是我们在使用时不可能出现这种情况因为state最小为2,state-1最小为1,避免了这种情况;

至此结束!

其余Bitmap的各种操作,实现时说明。

实现:

/*** 位图类:* 1.最多可以储存(2^37-2^6)个2种状态值的点;* 2.理论上有(2^37-2^6)个位空间,实际上可能不能完全使用(见无参构造器中的介绍),* 故使用时所选的点的状态值种数(stateNum)最好满足 64% ⌈ log2(stateNum) ⌉ = 0,即stateNum的补码的位数正好是64的因数;(听不明白的话,自行体会)* 3.没有删除点的方法,只有增加,更新,查找操作;(问为什么没有删除方法的人不是脑子有病,就是脑子有病。。。)* 4.回答3中问题:首先不是实现不了,是没有必要,因为删除一个点需要在数组中通过移位来填充被删除的位置,由于数据量过大可能会极其浪费时间,* 而且位图本身存在的意义是表示海量数据中每一条数据的可能性,对于海量数据而言,出现极个别错误并不会影响其最终统计结果(对于偷跑,出错的数据仍然可以在下一层中进行重新过滤,要是听不懂就算了。。。),* 这里建议使用update方法将不需要的点置0,用于表示该点被弃用,具体的逻辑需要使用者自己去编辑;* 5.该类所花费的理论持久存储空间大约为260MB也就是elementData数组的大小的极限值,实际可能会多一点,但几乎可以忽略不计,最终实现存储2种状态值的点的数量大约20亿个,* 也可以自定义每个点的状态值种数stateNum和点的数量length最大可以设置成(2^63-1)个,但最终要满足 length > ⌈ log2(stateNum) ⌉(向上取整) / 64 * Integer.MAX_VALUE,* 对于日常使用一定是足够的;(在这里,总有杠精会问超过了怎么办?就算是2种状态值的点有20亿个话,我还是不够用啊?)* 6.回答5中的问题:你不会搞个数组吗?你不能多new几个一起用吗!!!*/
public class Bitmap {/**使用long型数组保存点*/private final long[] elementData;/** 位图中能存储的所有点的位数之和的最大值MAX_STATE_SIZE=137,438,953,408;* 64(MAX_STATE_SIZE=long类型的位数) * 0x7fffffff(int类型的最大值Integer.MAX_VALUE)= MAX_STATE_SIZE (2^37-2^6)*/
//    private static final long MAX_STATE_SIZE = 0x1fffffffc0L;//结果没用上,大意了。。。/*位图保存的点的状态种数*/private final long stateNum;/*存储一个位图保存的点的状态值所需要的位数*/private final int stateBitNum;/*位图可以储存的最大点数,初始化Bitmap类时指定*/private final long MAX_SIZE;/*一个long型变量可以保存的点的数量*/private final long numOfOneLong;/*该位图已经保存的点的数量*/private long size;/*** @param stateNum 位图中点的状态数量,state>1* @param length   位图大小,0<length*                 如果 length > ⌈ log2(stateNum) ⌉(向上取整) / 64 * Integer.MAX_VALUE,则说明该 Bitmap 类无法储存 length 个 stateNum 种数的点,抛出异常*/public Bitmap(long stateNum, long length) {if (stateNum > 1 && length > 0) {/*计算状态值需要多少位来表示*/stateBitNum = 64 - Long.numberOfLeadingZeros(stateNum - 1);/*计算一个long型里面最多可以存放几个状态值,如果不能整除的话,就舍弃余下位数,因为在数组中跨元素进行位值的存取计算极其复杂,故舍弃部分位空间,方便位图的各项操作*/numOfOneLong = 64 / stateBitNum;/*一个long型最多可以存储numOfOneLong个状态值,所以该位图最大能存取numOfOneLong*Integer.MAX_VALUE个点,如果需要存储的数量length大于位图的最大存储数(numOfOneLong*Integer.MAX_VALUE)就说明该类Bitmap无法满足要求,抛出异常*/if (length > numOfOneLong * Integer.MAX_VALUE)throw new RuntimeException("初始化的位图过大,无法存储!!!");this.stateNum = stateNum;MAX_SIZE = length;if (length % numOfOneLong == 0)elementData = new long[(int) (length / numOfOneLong)];/*如果length个点不能被数组正好放下,就需要多添加一个数组元素来存储点*/else elementData = new long[((int) (length / numOfOneLong)) + 1];} elsethrow new RuntimeException("位图类的初始化传入参数值中没有负整数!!!");}/*** 顺序添加点** @param state 点的状态* @return true表示添加成功,产生false的情况有:位图满了;状态值越界*/public boolean add(long state) {if (state > stateNum - 1 || state < 0 || size == MAX_SIZE)return false;int index = (int) (size / numOfOneLong);int left = (int) (size % numOfOneLong);elementData[index] |= state << (64 - stateBitNum * (1 + left));++size;return true;}public long find(long index) {if (index < 0 || index > size - 1)return -1;/*计算数组中哪一个元素保存着index索引对应的点*/int arrayIndex = (int) (index / numOfOneLong);/*计算元素中从哪一位开始的位保存着index索引对应的点*/int elementIndex = (int) (index % numOfOneLong);/*通过左移,清空掉左边无用的位,再通过无符号右移清空掉右边无用的位,最终正好是需要查找的index位置对应的state值*/return elementData[arrayIndex] << (stateBitNum * elementIndex) >>> (64 - stateBitNum);}public boolean update(long index, long state) {if (index < 0 || index > size - 1 || state > stateNum - 1 || state < 0)return false;int arrayIndex = (int) (index / numOfOneLong);int left = (int) (index % numOfOneLong);elementData[arrayIndex] |= state << (64 - stateBitNum * (1 + left));return true;}/*** 返回位图中点的状态数量*/public long getStateNum() {return stateNum;}/*** 返回位图可以存储的点的最大数量*/public long getMaxSize() {return MAX_SIZE;}/*** 返回位图中实际已使用的数量*/public long getSize() {return size;}/*** 辅助toString()方法,主要用于打印位图的具体显示*/private String elementDataToString() {StringBuilder result = new StringBuilder("[\n");for (long element : elementData) {String eleString = Long.toBinaryString(element);StringBuilder one = new StringBuilder();for (int i = 0; i < 64 - eleString.length(); i++)one.append("0");one.append(eleString);for (int i = 0; i < numOfOneLong + 1; i++)one.insert((stateBitNum + 1) * i, ',');result.append(one.substring(1, one.lastIndexOf(","))).append(",\n");}return result.append("]").toString();}@Overridepublic String toString() {return "Bitmap{\n" +"elementData=" + elementDataToString() +", \nstateNum=" + stateNum +", \nstateBitNum=" + stateBitNum +", \nMAX_SIZE=" + MAX_SIZE +", \nsize=" + size +"\n}";}
}

测试:

public class test {public static void main(String[] args) {Bitmap bitmap = new Bitmap(16, 10);System.out.println(bitmap.add(2));System.out.println(bitmap.add(3));System.out.println(bitmap.add(4));System.out.println(bitmap.add(5));System.out.println(bitmap);System.out.println(bitmap.update(3, 7));System.out.println(bitmap);System.out.println(bitmap.find(3));}
}

总结:

位图类:
1. 最多可以储存(2^37-2^6)个2种状态值的点;

2. 理论上有(2^37-2^6)个位空间,实际上可能不能完全使用(见无参构造器中的介绍),故使用时所选的点的状态值种数(stateNum)最好满足 64% ⌈ log2(stateNum) ⌉ = 0,即stateNum的补码的位数正好是64的因数;(听不明白的话,自行体会)

3. 没有删除点的方法,只有增加,更新,查找操作;(问为什么没有删除方法的人不是脑子有病,就是脑子有病。。。)

4. 回答3中问题:首先不是实现不了,是没有必要,因为删除一个点需要在数组中通过移位来填充被删除的位置,由于数据量过大可能会极其浪费时间,而且位图本身存在的意义是表示海量数据中每一条数据的可能性,对于海量数据而言,出现极个别错误并不会影响其最终统计结果(对于偷跑,出错的数据仍然可以在下一层中进行重新过滤,要是听不懂就算了。。。),这里建议使用update方法将不需要的点置0,用于表示该点被弃用,具体的逻辑需要使用者自己去编辑;

5. 该类所花费的理论持久存储空间大约为260MB也就是elementData数组的大小的极限值,实际可能会多一点,但几乎可以忽略不计,最终实现存储2种状态值的点的数量大约20亿个,也可以自定义每个点的状态值种数stateNum和点的数量length最大可以设置成(2^63-1)个,但最终要满足 length > ⌈ log2(stateNum) ⌉(向上取整)/ 64 * Integer.MAX_VALUE,对于日常使用一定是足够的;(在这里,总有杠精会问超过了怎么办?就算是2种状态值的点有20亿个话,我还是不够用啊?)

6. 回答5中的问题:你不会搞个数组吗?你不能多new几个一起用吗!!!

位图Bitmap(基于Java实现)相关推荐

  1. java位图_位图Bitmap及其Java实现

    举个例子,如下图,如果我们想要存放 0,2,4,5,10,11,12,14,15这几个数字,如果采用普通存储方式,若4位表示一个数字,这9个数字需要4*9=36位,至少36位才能存储这些数据. 如果采 ...

  2. 基于java+Socket+sqlserver的油液远程监控系统软件设计

    基于java+Socket+sqlserver的油液远程监控系统软件设计 1.启动被监控端,打开指定的UDP端口号.用于读取命令. 2.被监控端读取命令(命令格式为ordername:port)ord ...

  3. java毕业设计——基于java+TCP+UDP的局域网聊天室系统设计与实现(毕业论文+程序源码)——局域网聊天室系统

    基于java+TCP+UDP的局域网聊天室系统设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+TCP+UDP的局域网聊天室系统设计与实现,文章末尾附有本毕业设计的论文和源码下载地 ...

  4. java毕业设计——基于java+Socket+sqlserver的远程监控系统软件设计与实现(毕业论文+程序源码)——远程监控系统

    基于java+Socket+sqlserver的远程监控系统软件设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+Socket+sqlserver的远程监控系统软件设计与实现,文章 ...

  5. 算法与数据结构——位图BitMap

    一.介绍 1.1 什么是位图 在Java中有四种整数类型:byte.short.int.long. 1 byte(字节) = 8 bit(位) 1 int = 4 byte = 32 bit 所以我们 ...

  6. [源码和文档分享]基于java 的仿QQ聊天工具

    一 需求分析 本系统是基于java开发的聊天室.有用户注册.用户登陆.修改密码.忘记密码.添加好友.用户聊天.群聊功能.如果服务器还没有启动,则客户端是不可以登陆.注册.忘记密码,如果在运行过程中,服 ...

  7. Lucene:基于Java的全文检索引擎简介(转载)

    Lucene是一个基于Java的全文索引工具包. 基于Java的全文索引引擎Lucene简介:关于作者和Lucene的历史 全文检索的实现:Luene全文索引和数据库索引的比较 中文切分词机制简介:基 ...

  8. java旅游网站毕业论文,基于JAVA技术的旅游网站的开发.doc

    摘要: 这次毕设主要是为了实现基于JAVA技术的旅游网站的开发,方便人们近距离的出行游玩.网站的开发过程中用到了很多方法技术,最主要的是JAVA技术,用于编写后台的功能实现代码:框架采用的是Sprin ...

  9. 基于Java的webapp_几乎所有基于Java的web app都需要Model 2 (part II) (转)

    几乎所有基于Java的web app都需要Model 2 (part II) (转)[@more@] 几乎所有基于的 app都需要Model 2 当Web容器从客户端接收到一个请求时,控制流开始运行. ...

最新文章

  1. python并发之协程_python并发编程之协程
  2. Android Studio 中的FindBugs插件使用,轻松帮你发现Bug (转)
  3. 安卓linux环境 查看进程,查看基于Android 系统单个进程内存、CPU使用情况的几种方法...
  4. 【Python科学计算系列】行列式
  5. 狗窝里的小日子- 3 ...
  6. oracle进入rman报错,Oracle 11g单实例RMAN恢复到Oracle 11g RAC
  7. Android 自定义组合控件小结
  8. [POI2014]Freight
  9. 找不到或是无法加载主类 不是原创
  10. fiddler html 保存,Fiddler的几个简单设置
  11. 算法笔记练习 题解合集
  12. orbslam2 流程图_一种基于ORBSLAM2的八叉树建图方法与流程
  13. html三角形正方形代码,用CSS画三角形,纯CSS绘制三角形的代码
  14. 【高等数学笔记】二元函数连续、可微、偏导数存在、偏导数连续、任意方向导数存在的关系
  15. 从招股书看蚂蚁集团的技术底色
  16. 选择适当的NFC标签类型
  17. 合辑 | 必知的200+基础技术词汇,你知道多少?
  18. 1、R5为ISP,只能进行ip地址配置:其所有接口配置为公有ip地切 2、R1与R5间使用PPP的PAP认证,R5为主认证方;R2与R5间使用PPP的chap认证 R5为主认证方;R3与R5间使用H
  19. 微信小程序 通过 ThorUI 框架 实现 选择城市
  20. linux下安装小企鹅输入法简单方法(转载)

热门文章

  1. Windows系统内远程控制软件VNC安装使用教程
  2. SQL SERVER高级知识梳理一:语法,函数,系统表笔记
  3. 私钥创建,申请证书,吊销证书,创建自签名证书
  4. Python数据分析师面试之“完美世界”数据分析师实习生
  5. HTML表单控件之基础介绍
  6. 同济大学c语言,同济大学c语言复习.ppt
  7. BurpSuite实战三之使用BurpSuite代理
  8. 我为何向蔡元培老校长三鞠躬?
  9. matlab吞吐量仿真,5G PDSCH信道吞吐量matlab仿真
  10. 钱真的能买到快乐吗?