计算机最小单位

在计算机中最小的数据单位是bit(比特),bit是二进制数的一位,包含的信息只有两种状态:0和1

常见数据类型长度转换

1 byte = 8 bit
1 char = 2 byte = 16 bit
1 short = 2 byte = 16 bit
1 int = 4 byte = 32 bit
1 long = 8 byte = 64 bit
1 float = 4 byte = 32 bit
1 double = 8 byte = 64 bit

问题思考

思考一个问题,如何在4亿个整数(整数值范围在0~3亿之间)中判断某个整数是否存在?而目前只有一台计算机,内存大小为500M。

最简单的方案就是开一个大小为3亿的数组(int[] data = new int[300000000]),如果100存在,则data[100] = 1,判断某个数是否存在只需要判断该数组中该下标位置上的值是否为1,该数组占用的大小为:

300000000*4 /1024 / 1024 = 1144.4 MB

这显然超出了机器的内存,而且该方案中,int占32bit,而我们记录只需要1个bit,白白浪费了31bit空间,那么能不能只用bit的类型来存储呢?当然是可以

什么是BitMap

BitMap一种数据结构,代表了有限域中的稠集,每一个元素至少出现一次……学术上的定义个人也很难理解,其实Bitmap的基本思想就是用一个bit位来标记某个元素对应的信息(其信息量大小为1bit)由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省空间。

  1. /*
  2. * @Author 松岛安
  3. * */
  4. public class BitMap {
  5. byte[] bits;
  6. int max;
  7. public BitMap(int max){
  8. this.bits = new byte[(max >> 3)+1];
  9. this.max = max;
  10. }
  11. public void add(int n){
  12. //落在哪个数组上
  13. int index = n >> 3;
  14. //落在byte的哪一位上
  15. int loc = n & 7;
  16. //在loc位上置1,做或运算,有1则为1
  17. bits[index] |= (1<<loc);
  18. }
  19. public void delect(int n){
  20. int index = n>>3;
  21. int loc = n & 7;
  22. if(isExit(n)){
  23. int delete = (1<<loc);
  24. //做异或运算,相同则为0,不同则为1
  25. bits[index] ^= delete;
  26. }
  27. }
  28. //查询某个数是否存在
  29. public boolean isExit(int n){
  30. int index = n >> 3;
  31. int loc = n & 7;
  32. int exit = 1 << loc;
  33. int flag = bits[index] & exit;
  34. return !(flag == 0);
  35. }
  36. }

上面代码中,我们用一个byte[]数组实现BitMap,数组的长度取决于所有要查找整数中最大值max,1 byte = 8 bit,数组的长度 length = max/8 + 1,整数落在byte中的哪一个bit上,可以通过取余(%)运算获得,由于byte = 8 bit(2的整数倍),可以通过与通过与运算计算

插入

当插入整数12时,首先需要计算12落在数组下标为多少的位置,通过位运算计算(1100 >> 3 = 1)落在byte[1] 的位置,在计算落在byte中哪一个bit上(1100 & 111 = 100 = 4),计算得到落在向左偏移量为4的bit上
微信截图_20210719140608.png
最后只需或运算即可插入

00000000 | (1<<4) =00000000 | 00010000 = 00010000

查找

查找12是否存在,同样找到12落在哪个bit上,接着进行与运算

00010000 & (1 << 4) = 00010000 & 00010000 = 00010000

若结果为0则不存在,结果不为0则存在

删除

删除12,同样找到12对应落在哪个bit上,接着进行异或运算(相同为0,不同为1)

00010000 ^ (1<<4) = 00010000 ^ 00010000 = 00000000

微信截图_20210719141103.png

此时回到一开始的问题,在4亿个整数(整数值范围在0~3亿之间)中判断某个整数是否存在,我们只需要创建长度为 300000000>>3 的byte[ ]数组,即可覆盖0~3亿范围的整数范围,判断其是否存在只需要检查其对应的bit上是否为1即可,空间开销为:

300000000 / 8 /1024 /1024 = 35.8 MB

我们只需要35.8MB的空间就可以表示3亿的数据量,是不是很妙!

如何判断某个数是否重复?

BitMap通过一个bit来判断元素是否存在,如果要判断某个元素是否重复,则需要在此基础上做一点变化。我们用 2个bit 的大小来表示某个元素的三种状态
00:元素不存在
01:元素置存在一个
11:元素重复出现

我们还是以 byte[] 数组作为存储,由于每个元素的状态需要2bit表示,所以空间消耗为原来BitMap的两倍,原来一个byte能表示8个元素,现在只能表示4个

  1. /*
  2. * @Author 松岛安
  3. * */
  4. public class BitMapRepeat {
  5. byte[] bits;
  6. int max;
  7. public BitMapRepeat(int max){
  8. this.bits = new byte[(max >> 2)+1];
  9. this.max = max;
  10. }
  11. /*
  12. * 检查元素是否重复
  13. * */
  14. public boolean isRepeat(int n){
  15. int index = n >> 2;
  16. //在原来基础上左移原来的长度
  17. int loc = (n & 3) << (n & 3);
  18. int repeat = 1 << (loc+1);
  19. int flag = bits[index] & repeat;
  20. return !(flag == 0);
  21. }
  22. /*
  23. * 检查元素是否存在
  24. * */
  25. public boolean isExit(int n){
  26. int index = n >> 2;
  27. int loc = (n & 3) << (n & 3);
  28. int exit = 1 << loc;
  29. int flag = bits[index] & exit;
  30. return !(flag == 0);
  31. }
  32. /*
  33. * 添加某个元素
  34. * */
  35. public void add(int n){
  36. int index = n >> 2;
  37. int loc = (n & 3) << (n & 3);
  38. //如果元素存在
  39. if(isExit(n)){
  40. //将元素所在的bit位置上左移1位的bit置为1
  41. int repeat = 1 << (loc+1);
  42. bits[index] |= repeat;
  43. }else {
  44. int add = 1 << loc;
  45. bits[index] |= add;
  46. }
  47. }
  48. /*
  49. * 删除某个元素
  50. * */
  51. public void delete(int n){
  52. int index = n >> 2;
  53. int loc = (n & 3) << (n & 3);
  54. //如果元素存在
  55. if(isExit(n)){
  56. //如果元素有重复,将11置为00,即全部都删除
  57. if(isRepeat(n)){
  58. int delete = (1 << loc) | (1 << (loc + 1));
  59. bits[index] ^= delete;
  60. }else {
  61. int delete = 1 << loc;
  62. bits[index] ^= delete;
  63. }
  64. }
  65. }
  66. }