原理

BitSet是位操作的对象,值只有0或1即false和true,内部维护了一个long数组,初始只有一个long,所以BitSet最小的size是64,当随着存储的元素越来越多,BitSet内部会动态扩充,最终内部是由N个long来存储,这些针对操作都是透明的。

用1位来表示一个数据是否出现过,0为没有出现过,1表示出现过。使用用的时候既可根据某一个是否为0表示,此数是否出现过。

一个1G的空间,有 8102410241024=8.5810^9bit,也就是可以表示85亿个不同的数。

注意:在没有外部同步的情况下,多个线程操作一个BitSet是不安全的。

例子

比如有一堆数字,需要存储,source=[3,5,6,9]

用int就需要4*4个字节。

java.util.BitSet可以存true/false。

如果用java.util.BitSet,则会少很多,其原理是:

1,先找出数据中最大值maxvalue=9

2,声明一个BitSet bs,它的size是maxvalue+1=10

3,遍历数据source,bs[source[i]]设置成true.

最后的值是:

(0为false;1为true)

bs [0,0,0,1,0,1,1,0,0,1]

3, 5,6, 9

这样一个本来要int型需要占4字节共32位的数字现在只用了1位!

比例32:1

这样就省下了很大空间

通常用在数据统计、分析的领域。

初始化逻辑

初始化大小 默认就一个long元素,逻辑如下:

  1. private final static int ADDRESS_BITS_PER_WORD = 6;
  2. private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
  3. //用来开辟bit位空间
  4. private long[] words;
  5. //当前long数组的大小
  6. private transient int wordsInUse = 0;
  7. public BitSet() {
  8. initWords(BITS_PER_WORD);
  9. sizeIsSticky = false;
  10. }
  11. private void initWords(int nbits) {
  12. words = new long[wordIndex(nbits-1) + 1];
  13. }
  14. //bitIndex除去64(bitIndex >> 6 )得到会落到long数组的index;
  15. private static int wordIndex(int bitIndex) {
  16. return bitIndex >> ADDRESS_BITS_PER_WORD;
  17. }

BitSet的基本运算

  1. public class BitUtils {
  2. /**
  3. * 获取运算数指定位置的值<br>
  4. * 例如: 0000 1011 获取其第 0 位的值为 1, 第 2 位 的值为 0<br>
  5. *
  6. * @param source
  7. * 需要运算的数
  8. * @param pos
  9. * 指定位置 (0<=pos<=7)
  10. * @return 指定位置的值(0 or 1)
  11. */
  12. public static byte getBitValue(byte source, int pos) {
  13. return (byte) ((source >> pos) & 1);
  14. }
  15. /**
  16. * 将运算数指定位置的值置为指定值<br>
  17. * 例: 0000 1011 需要更新为 0000 1111, 即第 2 位的值需要置为 1<br>
  18. *
  19. * @param source
  20. * 需要运算的数
  21. * @param pos
  22. * 指定位置 (0<=pos<=7)
  23. * @param value
  24. * 只能取值为 0, 或 1, 所有大于0的值作为1处理, 所有小于0的值作为0处理
  25. *
  26. * @return 运算后的结果数
  27. */
  28. public static byte setBitValue(byte source, int pos, byte value) {
  29. byte mask = (byte) (1 << pos);
  30. if (value > 0) {
  31. source |= mask;
  32. } else {
  33. source &= (~mask);
  34. }
  35. return source;
  36. }
  37. /**
  38. * 将运算数指定位置取反值<br>
  39. * 例: 0000 1011 指定第 3 位取反, 结果为 0000 0011; 指定第2位取反, 结果为 0000 1111<br>
  40. *
  41. * @param source
  42. *
  43. * @param pos
  44. * 指定位置 (0<=pos<=7)
  45. *
  46. * @return 运算后的结果数
  47. */
  48. public static byte reverseBitValue(byte source, int pos) {
  49. byte mask = (byte) (1 << pos);
  50. return (byte) (source ^ mask);
  51. }
  52. /**
  53. * 检查运算数的指定位置是否为1<br>
  54. *
  55. * @param source
  56. * 需要运算的数
  57. * @param pos
  58. * 指定位置 (0<=pos<=7)
  59. * @return true 表示指定位置值为1, false 表示指定位置值为 0
  60. */
  61. public static boolean checkBitValue(byte source, int pos) {
  62. source = (byte) (source >>> pos);
  63. return (source & 1) == 1;
  64. }
  65. /**
  66. * 入口函数做测试<br>
  67. *
  68. * @param args
  69. */
  70. public static void main(String[] args) {
  71. // 取十进制 11 (二级制 0000 1011) 为例子
  72. byte source = 11;
  73. // 取第2位值并输出, 结果应为 0000 1011
  74. for (byte i = 7; i >= 0; i--) {
  75. System.out.printf("%d ", getBitValue(source, i));
  76. }
  77. // 将第6位置为1并输出 , 结果为 75 (0100 1011)
  78. System.out.println("\n" + setBitValue(source, 6, (byte) 1));
  79. // 将第6位取反并输出, 结果应为75(0100 1011)
  80. System.out.println(reverseBitValue(source, 6));
  81. // 检查第6位是否为1,结果应为false
  82. System.out.println(checkBitValue(source, 6));
  83. // 输出为1的位, 结果应为 0 1 3
  84. for (byte i = 0; i < 8; i++) {
  85. if (checkBitValue(source, i)) {
  86. System.out.printf("%d ", i);
  87. }
  88. }
  89. }
  90. }

BitSet的应用一——排序

  1. /**
  2. * 问题重述:一个最多包含n个正整数的文件,每个数都小于n,其中n=107,并且没有重复。
  3. * 最多有1MB内存可用。要求用最快方式将它们排序并按升序输出。
  4. */
  5. import java.util.BitSet;
  6. import java.util.Scanner;
  7. /**
  8. * 解决思路
  9. * 将文件中的数读入,把数字对应的bit位设置为1,最后,将bit位为1的按序输出。
  10. */
  11. public class SortByBit {
  12. public static void main(String args[]) {
  13. //输入数字
  14. int n;
  15. Scanner sc = new Scanner(System.in);
  16. n = sc.nextInt();
  17. BitSet bitSet = new BitSet();
  18. for (int i = n; i>0;i--) {
  19. bitSet.set(sc.nextInt());
  20. }
  21. //输出
  22. for (int i = bitSet.size(); i>0; i--) {
  23. if (bitSet.get(i))
  24. System.out.print(i + " ");
  25. }
  26. }
  27. }
  28. //输出
  29. 3
  30. 1 20 2
  31. 20 2 1

应用二——字符串判重

  1. BitSet bitSet = new BitSet(Integer.MAX_VALUE);//hashcode的值域
  2. //0x7FFFFFFF (int类型的最大值,第一位是符号位,可用Integer.MAX_VALUE代替)
  3. String url = "http://baidu.com/a";
  4. int hashcode = url.hashCode() & 0x7FFFFFFF;
  5. bitSet.set(hashcode);
  6. System.out.println(bitSet.cardinality()); //状态为true的个数
  7. System.out.println(bitSet.get(hashcode)); //检测存在性
  8. bitSet.clear(hashcode); //清除状态

为什么使用long,不用int?

JDK选择long数组作为BitSet的内部存储结构是出于性能的考虑,因为BitSet提供and和or这种操作,需要对两个BitSet中的所有bit位做and或者or,实现的时候需要遍历所有的数组元素。使用long能够使得循环的次数降到最低,所以Java选择使用long数组作为BitSet的内部存储结构。

从数据在栈上的存储来说,使用long和byte基本是没有什么差别的,除了编译器强制地址对齐的时候,使用byte最多会浪费7个字节(强制按照8的倍数做地址对其),另外从内存读数组元素的时候,也是没有什么区别的,因为汇编指令有对不同长度数据的mov指令。所以说,JDK选择使用long数组作为BitSet的内部存储结构的根本原因就是在and和or的时候减少循环次数,提高性能。

Java1.8-BitSet源码分析

https://www.jianshu.com/p/91d75bf588b8

大白话讲解 BitSet的更多相关文章

  1. 大白话讲解Promise(二)理解Promise规范

    上一篇我们讲解了ES6中Promise的用法,但是知道了用法还远远不够,作为一名专业的前端工程师,还必须通晓原理.所以,为了补全我们关于Promise的知识树,有必要理解Promise/A+规范,理解 ...

  2. 大白话讲解Promise(一)

    去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正式规范.作为ES6中最重要的特性之一,我们有必要掌握并理解透彻.本文将由浅到深,讲解Promise的基本 ...

  3. 用通俗易懂的大白话讲解Map/Reduce原理

    Hadoop简介 Hadoop就是一个实现了Google云计算系统的开源系统,包括并行计算模型Map/Reduce,分布式文件系统HDFS,以及分布式数据库Hbase,同时Hadoop的相关项目也很丰 ...

  4. 大白话讲解Promise

    去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正式规范.作为ES6中最重要的特性之一,我们有必要掌握并理解透彻.本文将由浅到深,讲解Promise的基本 ...

  5. [转]大白话讲解Promise(一)

    http://www.cnblogs.com/lvdabao/p/es6-promise-1.html 去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正 ...

  6. 适合小白的大白话讲解--->Git与Github的区别

    本文由 伯乐在线 - 听风 翻译,艾凌风 校稿.未经许可,禁止转载!英文出处:Red Radger.欢迎加入翻译组. 本文旨在使用通俗易懂的文字,讲解版本控制背后的理论,以便你能对程序员们如何工作有个 ...

  7. 大白话讲解Promise(三)搞懂jquery中的Promise

    前两篇我们讲了ES6中的Promise以及Promise/A+规范,在Promise的知识体系中,jquery当然是必不可少的一环,所以本篇就来讲讲jquery中的Promise,也就是我们所知道的D ...

  8. 花20分钟写的-大白话讲解如何给github上项目贡献代码

    原文地址:http://site.douban.com/196781/widget/notes/12161495/note/269163206/ 本文献给对git很迷茫的新手,注意是新手,但至少会点基 ...

  9. EM算法 大白话讲解

    假设有一堆数据点,它是由两个线性模型产生的.公式如下: 模型参数为a,b,n:a为线性权值或斜率,b为常数偏置量,n为误差或者噪声. 一方面,假如我们被告知这两个模型的参数,则我们可以计算出损失. 对 ...

随机推荐

  1. 结合suctf-upload labs-RougeMysql再学习

    这篇主要记录一下这道题目的预期解法 做这道题首先要在自己的vps搭建一个rouge mysql,里面要填写需要读取客户端的文件名,即我们上传的phar文件路径 先搭一个rouge mysql测试看看: ...

  2. Linux任务调度(8)

    crond任务调度: 是指系统在某个时间执行特定的命令或程序. 分类:1.系统工作,有些重要的工作必须周而复始地执行,如病毒扫描等:2.个别用户工作,个别用户可能希望执行某些程序,如mysql数据库备 ...

  3. Java之戳中痛点 - (8)synchronized深度解析

    概览: 简介:作用.地位.不控制并发的影响 用法:对象锁和类锁 多线程访问同步方法的7种情况 性质:可重入.不可中断 原理:加解锁原理.可重入原理.可见性原理 缺陷:效率低.不够灵活.无法预判是否成功 ...

  4. Python+Selenium - Web自动化测试(二):元素定位

    前言 前面已经把环境搭建好了,现在开始使用 Selenium 中的 Webdriver 框架编写自动化代码脚本,我们常见的在浏览器中的操作都会有相对应的类方法,这些方法需要定位才能操作元素,不同网页的 ...

  5. JVM内存结构与垃圾回收总结

    1.JVM内存模型 JVM只不过是运行在你系统上的另一个进程而已,这一切的魔法始于一个java命令.正如任何一个操作系统进程那样,JVM也需要内存来完成它的运行时操作.记住:JVM本身是硬件的一层软件 ...

  6. Python爬虫爬取全书网小说,程序源码+程序详细分析

    Python爬虫爬取全书网小说教程 第一步:打开谷歌浏览器,搜索全书网,然后再点击你想下载的小说,进入图一页面后点击F12选择Network,如果没有内容按F5刷新一下 点击Network之后出现如下 ...

  7. js的真值与假值

    假值 结果为 false 的值称为 假值.例如,空字符串 "" 为假值,因为在布尔表达式中,"" 等于 false. false == 0返回:true fal ...

  8. Fire Balls 05——子弹的命中及后续效果

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  9. 简明Python教程-函数联系笔记

    1.实参与形参 在定义函数时给定的名称称作"形参",再调用函数时你所提供给函数的值称作“实参” 2.局部变量 所有变量的作用域是它们被定义的块,从定义它们的名字的定义点开始. 3. ...

  10. 通过代码审计找出网站中的XSS漏洞实战(三)

    一.背景 笔者此前录制了一套XSS的视频教程,在漏洞案例一节中讲解手工挖掘.工具挖掘.代码审计三部分内容,准备将内容用文章的形式再次写一此,前两篇已经写完,内容有一些关联性,其中手工XSS挖掘篇地址为 ...