工作中依赖外部团队使用了位运算对数据进行了转化和存储。
今天整理下关于位运算相关的内容。
符号 | 描述 | 运算规则 | |
---|---|---|---|
& | 与 | 两个位都为1时,结果才为1 | |
l | 或 | 两个位都为0时,结果才为0 | |
^ | 异或 | 两个位相同为0,相异为1 | 0^0=0 0^1=1 1^0=1 1^1=0 |
~ | 取反 | 0变1,1变0 | |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 | |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) | |
1) 源码中使用
位运算计算速度快。 LIst,map源码中使用。 如arrayList 扩容
2)状态位标识
商品的属性有很多: 是否新品,是否虚拟商品,是否图书,是否支持配送,是否参加营销活动。
10110; 我们可以表示 新品图书且支持配送。
3)数据状态整合
比如本文背景中说到的,将主类型 移位后,和子类型运算,整合成一个数值。
-
举个例子
记录员工本周(月)打卡情况。
场景: 记录员工本周(月)打卡情况。
实现: 使用位运算-实现统计周打卡的类。1)签到方法;取消签到方法;统计总数;获取签到数据;查看某天是否签到等 2)定义枚举,原来标识状态。本例中标识 每天的签到情况。
/**
* 记录周打卡的实现类
*/
public class WeekSign {
//本周签到状态
private static int status;
//设置签到状态
public static void setStatus(int status){
WeekSign.status = status;
}
/**
* 获取周签到记录
* @return
*/
public static String getWeeKSignRecord(){
System.out.println("签到状态值为:"+status);
return Integer.toBinaryString(status);
}
/**
* 周签到总数
* @return
*/
public static int getWeeKSignRecordCount(){
return Integer.bitCount(status);
}
/**
* 添加签到 (打标)
* @param
*/
public static void addSign(WeekSignEnum xDay){
if( (status & xDay.getValue()) == xDay.getValue() ){
System.out.println(xDay.getName()+"已签到,无需重复签到");
return;
}
status = status | xDay.getValue();
}
/**
* 取消签到(取消打标)
* @param xDay
*/
public static void removeSign(WeekSignEnum xDay){
if( (status & xDay.getValue()) != xDay.getValue() ){
System.out.println(xDay.getName()+"未签到,无需取消签到");
return;
}
//异或运算
status = status ^ xDay.getValue();
}
/**
* 判断哪天是否签到 (判断某个标识)
* @param xDay
*/
public static void getXdayIsSign(WeekSignEnum xDay){
if( (status & xDay.getValue()) == xDay.getValue() ){
System.out.println(xDay.getName()+":签到了");
}else{
System.out.println(xDay.getName()+":缺勤");
}
}
/**
* 内部枚举定义
*/
enum WeekSignEnum {
day01Sign("周一",1),
day02Sign("周二",2<<0),
day03Sign("周三",2<<1),
day04Sign("周四",2<<2),
day05Sign("周五",2<<3),
day06Sign("周六",2<<4),
day07Sign("周日",2<<5);
private String name;
//某天签到的标记值
private int value;
WeekSignEnum(String name, int value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
}
}
测试“签到打卡”类
public void multiBooleanStatusBitStore() {
//WeekSign.setStatus(WeekSign.WeekSignEnum.day01Sign.getValue() | WeekSign.WeekSignEnum.day03Sign.getValue());
//打卡签到
WeekSign.addSign(WeekSign.WeekSignEnum.day01Sign);
WeekSign.addSign(WeekSign.WeekSignEnum.day02Sign);
WeekSign.addSign(WeekSign.WeekSignEnum.day03Sign);
WeekSign.addSign(WeekSign.WeekSignEnum.day04Sign);
WeekSign.addSign(WeekSign.WeekSignEnum.day05Sign);
WeekSign.addSign(WeekSign.WeekSignEnum.day07Sign);
//重复打卡
WeekSign.addSign(WeekSign.WeekSignEnum.day05Sign);
//取消签到
WeekSign.removeSign(WeekSign.WeekSignEnum.day02Sign);
//重复取消签到
WeekSign.removeSign(WeekSign.WeekSignEnum.day02Sign);
//总的情况
System.out.println("本周签到记录(二进制):" + WeekSign.getWeeKSignRecord());
System.out.println("本周一共签到天数:" + WeekSign.getWeeKSignRecordCount());
//判断某天 是否签到
WeekSign.getXdayIsSign(WeekSign.WeekSignEnum.day03Sign);
WeekSign.getXdayIsSign(WeekSign.WeekSignEnum.day05Sign);
}
效果
Q: 有 1000 个一模一样的瓶子,其中有 999 瓶是普通的水,有1瓶是毒药。现在,如果检验出哪个瓶子里有毒药,(一定时间内)最少需要几只小老鼠试毒。
A: log2 1000 = 10只。
解释: 以8瓶水为例,log2 8 = 3只。
将水瓶编号,将(二进制)对应位数为1的水喂给同一个小老鼠。一定时间后 看有哪些小老鼠GG了。
如:B和C 小老鼠G了, 说明 【0 1 1】 水瓶有毒。