实战 | 秀儿,如何用一个字段表示八个状态啊
作者:互联网
实战 | 秀儿,如何用一个字段表示八个状态啊
点击上方“java进阶架构师”,选择右上角“置顶公众号”
20大进阶架构专题每日送达
我就是那个人见人爱的 锦成同学,我是java进阶架构师社区的特邀作者,今天的文章由我为大家带来一波秀儿搬的操作......
一、惊现问题
有一天,突然来了一个需求,问小明提交了哪些课程的作业?
A:语文 B:数学 C:英语 D:物理 E:化学 F:生物 G:历史 H:地理 .....
- 这么多课程.....我要8个字段? 万一还有课程呢?我还要再加字段?
- 我肯定不慌啊,我一个字段搞定。
二、解决之道
通过一个 int或者long 字段,来添加多个 标志或者状态. 一个int或者long字段,能够管理多个标记(状态)值. 如此神奇的操作怎样实现的呢? 答案就是通过位运算来实现.
- 像这种,独立状态(标记)之间相互组合可以产生新的状态(标记),且每个独立状态(标记)只有true或者false值的,我们可以使用位状态的概念来管理这些状态.
- 它的核心思想就是将, int 数值看做是 二进制数位表示.如果有四个状态就可以像这样 0000,用四位二进制表示,每一个二进制位都可以表示一种状态. 然后通过 位运算,来提取或添加标记位.四位对应的组合状态有16个. 而我们,只需要通过一个int变量就能够管理这些状态.
- 当参与的状态(标记)越多时,如果使用单独的标记变量,就需要生成越多的变量,而用位域,这种独立状态为不管有多少个,都可以用一个变量表示.int类型最多存放32个独立状态.
- 位操作基础:
java中提供的基础位运算符有 与(&),或(|),非(~),异或(^),左移<<,右移(>>)和无符号右移(>>>).
除了位非(~)是一元操作符外,其它的都是二元操作符。 - 下面只介绍本文中,使用到的位操作:
1.位与
A & B : A和B对应的二进制数位都为1时,结果才为1,其他情况为0.
A = 001101 // 13
B = 100101 // 37
A & B = 000101 // 5
2.位或
A | B : A和B对应的二进制数位都为0时,结果才为0,其他情况为1.
A = 001101 // 13
B = 100101 // 37
A | B = 101101 // 45
3.位非
~A : 将a的二进制表示每一位进行取反操作,0变1,1变0.相当于相反数 - 1
A = 001101 // 13
~A = 11111111111111111111111111110010 // int32位,补码表示,第一位为符号位
// 根据上诉补码转原码为
// 10000000000000000000000000001110 // -14
4.左移操作
A << B:将A的二进制表示的每一位向左移B位, 左边超出的位截掉,右边不足的位补0。在取值范围内,移动一位相当于乘2.
A = 001101 // 13
A << 1 = 011010 // 26
三、实战操作
/**
* @author LiJing
* @ClassName: BitStatusUtils
* @Description: 位运算处理状态的工具类
* @date 2019/9/21
* @time 10:38
*/
public class BitStatusUtils {
//A:语文 B:数学 C:英语 D:物理 E:化学 F:生物 G:历史 H:地理
// 二进制表示 0001 没有交任何作业
public static final int NONE = 1<<0; //默认
public static final int CHINESE = NONE<<1;//语文
public static final int MATH = NONE<<2;//数学
public static final int ENGLISH = NONE<<3;//英语
public static final int PHYSICS = NONE<<4;//物理
public static final int CHEMISTRY = NONE<<5;//化学
public static final int BIOLOGY = NONE<<6;//生物
public static final int HISTORY = NONE<<7;//历史
public static final int GEOGRAPHY = NONE<<8;//地理
public static final int ALL =NONE|CHINESE|MATH|ENGLISH|PHYSICS|CHEMISTRY|BIOLOGY|HISTORY|GEOGRAPHY;
/**
* @param status 所有状态值
* @param value 需要判断状态值
* @return 是否存在
*/
public static boolean hasStatus(long status, long value) {
return (status & value) != 0;
}
/**
* @param status 已有状态值
* @param value 需要添加状态值
* @return 新的状态值
*/
public static long addStatus(long status, long value) {
if (hasStatus(status, value)) {
return status;
}
return (status | value);
}
/**
* @param status 已有状态值
* @param value 需要删除状态值
* @return 新的状态值
*/
public static long removeStatus(long status, long value) {
if (!hasStatus(status, value)) {
return status;
}
return status ^ value;
}
/**是否交了含有全部状态
* @param status
* @return
*/
public static boolean hasAllStatus(long status) {
return (status & ALL) == ALL;
}
public static void main(String[] args) {
long status = addStatus(NONE, CHINESE);
System.out.println("小明交了语文作业:" + status);
status = addStatus(status, PHYSICS);
System.out.println("小明又交了物理作业:" + status);
status = addStatus(status, HISTORY);
System.out.println("小明还交了历史作业:" + status);
status = removeStatus(status, HISTORY);
System.out.println("小明撤销了历史作业:" + status);
System.out.println("小明是否交了语文作业:" + hasStatus(status, CHINESE));
System.out.println("小明是否交了历史作业:" + hasStatus(status, HISTORY));
System.out.println("小明是否交了生物作业:" + hasStatus(status, BIOLOGY));
System.out.println("小明是否交了全部作业:" + hasAllStatus(status));
}
}
测试结果:
小明交了语文作业:3
小明又交了物理作业:19
小明还交了历史作业:147
小明撤销了历史作业:19
小明是否交了语文作业:true
小明是否交了历史作业:false
小明是否交了生物作业:false
小明是否交了全部作业:false
四、完美小结
- 内存由字节组成.每个字节由8位bit组成,每个bit状态只能是0或1.
- 所谓位模式,无非就是变量所占用内存的所有bit的状态的序列
- 运用场景:
订单实体,我们可能涉及到的状态有,是否发货,是否审核,是否付款,是否接受等等.
棋牌游戏用户状态
棋盘的位图(棋盘模式)位置(域):上下左右 左上角 右上角 左下角 右下角 方位等待
用户是否提交多项认证资料等 - 优势:
占用内存少
具有高效的拷贝、设值、比较运算等
位并行运算的可能 -
不足:
难于调试
访问单独位的复杂性,易出错
难于扩展
不符合面向对象的风格
不能处理所有的任务;需要大量的工作 - 喜欢吗?喜欢就别愣着啊 点赞啊~~ 感谢大家关注我...据说点赞延年益寿哦。
———— e n d ————
微服务、高并发、JVM调优、面试专栏等20大进阶架构师专题请关注公众号【Java进阶架构师】后在菜单栏查看。
看到这里,说明你喜欢本文
你的转发,是对我最大的鼓励!在看亦是支持↓
标签:实战,小明,进阶,状态,int,八个,作业,二进制,秀儿 来源: https://blog.51cto.com/15009303/2553013