大白话讲解 BitSet
原理
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元素,逻辑如下:
private final static int ADDRESS_BITS_PER_WORD = 6;
private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
//用来开辟bit位空间
private long[] words;
//当前long数组的大小
private transient int wordsInUse = 0;
public BitSet() {
initWords(BITS_PER_WORD);
sizeIsSticky = false;
}
private void initWords(int nbits) {
words = new long[wordIndex(nbits-1) + 1];
}
//bitIndex除去64(bitIndex >> 6 )得到会落到long数组的index;
private static int wordIndex(int bitIndex) {
return bitIndex >> ADDRESS_BITS_PER_WORD;
}
BitSet的基本运算
public class BitUtils {
/**
* 获取运算数指定位置的值<br>
* 例如: 0000 1011 获取其第 0 位的值为 1, 第 2 位 的值为 0<br>
*
* @param source
* 需要运算的数
* @param pos
* 指定位置 (0<=pos<=7)
* @return 指定位置的值(0 or 1)
*/
public static byte getBitValue(byte source, int pos) {
return (byte) ((source >> pos) & 1);
}
/**
* 将运算数指定位置的值置为指定值<br>
* 例: 0000 1011 需要更新为 0000 1111, 即第 2 位的值需要置为 1<br>
*
* @param source
* 需要运算的数
* @param pos
* 指定位置 (0<=pos<=7)
* @param value
* 只能取值为 0, 或 1, 所有大于0的值作为1处理, 所有小于0的值作为0处理
*
* @return 运算后的结果数
*/
public static byte setBitValue(byte source, int pos, byte value) {
byte mask = (byte) (1 << pos);
if (value > 0) {
source |= mask;
} else {
source &= (~mask);
}
return source;
}
/**
* 将运算数指定位置取反值<br>
* 例: 0000 1011 指定第 3 位取反, 结果为 0000 0011; 指定第2位取反, 结果为 0000 1111<br>
*
* @param source
*
* @param pos
* 指定位置 (0<=pos<=7)
*
* @return 运算后的结果数
*/
public static byte reverseBitValue(byte source, int pos) {
byte mask = (byte) (1 << pos);
return (byte) (source ^ mask);
}
/**
* 检查运算数的指定位置是否为1<br>
*
* @param source
* 需要运算的数
* @param pos
* 指定位置 (0<=pos<=7)
* @return true 表示指定位置值为1, false 表示指定位置值为 0
*/
public static boolean checkBitValue(byte source, int pos) {
source = (byte) (source >>> pos);
return (source & 1) == 1;
}
/**
* 入口函数做测试<br>
*
* @param args
*/
public static void main(String[] args) {
// 取十进制 11 (二级制 0000 1011) 为例子
byte source = 11;
// 取第2位值并输出, 结果应为 0000 1011
for (byte i = 7; i >= 0; i--) {
System.out.printf("%d ", getBitValue(source, i));
}
// 将第6位置为1并输出 , 结果为 75 (0100 1011)
System.out.println("\n" + setBitValue(source, 6, (byte) 1));
// 将第6位取反并输出, 结果应为75(0100 1011)
System.out.println(reverseBitValue(source, 6));
// 检查第6位是否为1,结果应为false
System.out.println(checkBitValue(source, 6));
// 输出为1的位, 结果应为 0 1 3
for (byte i = 0; i < 8; i++) {
if (checkBitValue(source, i)) {
System.out.printf("%d ", i);
}
}
}
}
BitSet的应用一——排序
/**
* 问题重述:一个最多包含n个正整数的文件,每个数都小于n,其中n=107,并且没有重复。
* 最多有1MB内存可用。要求用最快方式将它们排序并按升序输出。
*/
import java.util.BitSet;
import java.util.Scanner;
/**
* 解决思路
* 将文件中的数读入,把数字对应的bit位设置为1,最后,将bit位为1的按序输出。
*/
public class SortByBit {
public static void main(String args[]) {
//输入数字
int n;
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
BitSet bitSet = new BitSet();
for (int i = n; i>0;i--) {
bitSet.set(sc.nextInt());
}
//输出
for (int i = bitSet.size(); i>0; i--) {
if (bitSet.get(i))
System.out.print(i + " ");
}
}
}
//输出
3
1 20 2
20 2 1
应用二——字符串判重
BitSet bitSet = new BitSet(Integer.MAX_VALUE);//hashcode的值域
//0x7FFFFFFF (int类型的最大值,第一位是符号位,可用Integer.MAX_VALUE代替)
String url = "http://baidu.com/a";
int hashcode = url.hashCode() & 0x7FFFFFFF;
bitSet.set(hashcode);
System.out.println(bitSet.cardinality()); //状态为true的个数
System.out.println(bitSet.get(hashcode)); //检测存在性
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的更多相关文章
- 大白话讲解Promise(二)理解Promise规范
上一篇我们讲解了ES6中Promise的用法,但是知道了用法还远远不够,作为一名专业的前端工程师,还必须通晓原理.所以,为了补全我们关于Promise的知识树,有必要理解Promise/A+规范,理解 ...
- 大白话讲解Promise(一)
去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正式规范.作为ES6中最重要的特性之一,我们有必要掌握并理解透彻.本文将由浅到深,讲解Promise的基本 ...
- 用通俗易懂的大白话讲解Map/Reduce原理
Hadoop简介 Hadoop就是一个实现了Google云计算系统的开源系统,包括并行计算模型Map/Reduce,分布式文件系统HDFS,以及分布式数据库Hbase,同时Hadoop的相关项目也很丰 ...
- 大白话讲解Promise
去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正式规范.作为ES6中最重要的特性之一,我们有必要掌握并理解透彻.本文将由浅到深,讲解Promise的基本 ...
- [转]大白话讲解Promise(一)
http://www.cnblogs.com/lvdabao/p/es6-promise-1.html 去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正 ...
- 适合小白的大白话讲解--->Git与Github的区别
本文由 伯乐在线 - 听风 翻译,艾凌风 校稿.未经许可,禁止转载!英文出处:Red Radger.欢迎加入翻译组. 本文旨在使用通俗易懂的文字,讲解版本控制背后的理论,以便你能对程序员们如何工作有个 ...
- 大白话讲解Promise(三)搞懂jquery中的Promise
前两篇我们讲了ES6中的Promise以及Promise/A+规范,在Promise的知识体系中,jquery当然是必不可少的一环,所以本篇就来讲讲jquery中的Promise,也就是我们所知道的D ...
- 花20分钟写的-大白话讲解如何给github上项目贡献代码
原文地址:http://site.douban.com/196781/widget/notes/12161495/note/269163206/ 本文献给对git很迷茫的新手,注意是新手,但至少会点基 ...
- EM算法 大白话讲解
假设有一堆数据点,它是由两个线性模型产生的.公式如下: 模型参数为a,b,n:a为线性权值或斜率,b为常数偏置量,n为误差或者噪声. 一方面,假如我们被告知这两个模型的参数,则我们可以计算出损失. 对 ...
随机推荐
- 使用.Net Core CLI命令dotnet new创建自定义模板
文章起源来自一篇博客:使用 .NET CORE 创建 项目模板,模板项目,Template - DeepThought - 博客园 之前使用Abp的时候就很认同Abp创建模板项目的方式.想不到.Net ...
- 微服务API通过ip可访问,域名不可访问问题分析
摘要 经常会有同学遇到api通过ip可以访问,但是通过域名却不可以访问.本篇文章总结了造成这种情况可能的原因. 因为与具体技术的选型.规则配置有关,所以没有深入讨论,只是列出可能性,仅供参考. 分析 ...
- Linux命令行中好用的快捷键
Ctrl + L:清屏,与clear同效: Ctrl + D:终结当前程序,向终端传递EOF(End Of File): Ctrl + A:光标移至一行命令开头: Ctrl + E:光标移至一行命令结 ...
- HDU 6315
题意略. 思路:本题的思路总的来说就是暴力 + 剪枝. 我们依然用线段树来维护: 定义结点node{ l , r , minn , contirbute} 分别为某个区间的左右端点,和该区间(b序列) ...
- Jenkins教程(三)添加凭据与流水线拉取Git代码
前言 本文旨在配置凭据.使用Git仓库中的Jenkinsfile与使用声明式流水线拉取Git代码 使用SVN等其他版本控制工具,请参考使用Pipeline-Syntax生成对应代码块 凭据(crede ...
- 第一篇 特征提取以及openvslam中的相关实现详解
参考尺度空间理论 金字塔 当用一个机器视觉系统分析未知场景时,计算机没有办法预先知道图像中物体尺度,因此,我们需要同时考虑图像在多尺度下的描述,获知感兴趣物体的最佳尺度.所以在很多时候,我们会在将图像 ...
- Mac下搭建go和beego开发环境
go安装 首先到golang的官网下载 直接双击安装包,安装 ,下一步,下一步....完成既可 验证安装是否成功 打开终端执行 输出 go version go1.11.5 darwin/amd64 ...
- JMeter特点&性能测试工具选型的原则&模拟压力的原理
1.JMeter自身的特点 1)开源.轻量级.更适合自动化和持续集成(100M左右,LoadRunner 4G左右) 2)学习难度大 3)资料少.全英文 2. 性能测试工具选型的原则 1)成本 a.工 ...
- CodeForces - 632E Thief in a Shop 完全背包
632E:http://codeforces.com/problemset/problem/632/E 参考:https://blog.csdn.net/qq_21057881/article/det ...
- 牛客暑假多校第二场 F trade
题意: 白兔有n个仓库,每个仓库有啊ai个货物,在每个仓库白兔可以装上任意数量的货物,也可以卸下任意数量的货物,现在有k个圆形信号阻隔器,然后有m个顾客下个一个订单,每个顾客的收货量有一个上限, 在每 ...