位移在枚举中的应用
最近在公司代码中发现一个枚举类中使用了位移来表示枚举值,不是很能理解为何这样设置,遂去问了度娘和老同事。大概意思就是存在多个枚举组合使用的情况下使用位移能很方便的分辨出是哪些枚举。
public enum BusinessType {
A("AAA", 1 << 0),
B("BBB", 1 << 1),
C("CCC", 1 << 2);
}
如上是一个产品线枚举,如果想判断某个产品是否同时属于产品线A和产品线B,可以这样子来判断:
Integer type = BusinessType.A.getCode() + BusinessType.B.getCode();
if ((obj.getBusinessType() & type) == type) {
}
举一反三,在检验场景中的使用
接上次的检验场景,表单对象中的用户可选的标签中有几个有冲突不能同时选择,在保存表单对象必须对这些情况进行检验。
1.构建枚举类
很幸运,公司代码中已经有用户选择标签的枚举类了,但是没有可供使用的位移枚举值,因此扩展一下,如下:
public enum Label {
A("AAA", 1 << 0),
B("BBB", 1 << 1),
C("CCC", 1 << 2);
D("DDD", 1 << 3);
}
2.设计冲突规则&计算校验和
比如,A和B不能同时选择校验规则,至少选择一个标签,当然也适用比较复杂的校验规则,如必须ABCD之间自由组合,这些规则可以按如下设计:
// AB不能同时选择
Integer ABConflictRule = Label.A.getCode() + Label.B.getCode();
// 至少选择一个
Integer leastOneCheckedRule = ABConflictRule + Label.C.getCode() + Label.D.getCode();
List<Integer> complexCheckRule = Lists.newArrayList(
Label.A.getCode(),
Label.B.getCode(),
Label.C.getCode(),
Label.D.getCode(),
);
冲突规则设计完毕之后,就需要计算所选择标签的校验和了,
int checkSum = 0;
for (Label label : query.getLabels()) {
checkSum += Lable.descOf(label.getDesc).getCode();
}
3.进行校验
对校验和和冲突规则使用位移运算,然后根据位移运算之后的结果判断是否冲突,如:
if ((ABConflictRule & checkSum) == ABConflictRule) {
System.out.println("AB冲突了");
}
if ((leastOneCheckedRule & checkSum) == 0) {
System.out.println("没有选择标签");
}
// 也可以这样
if (chcksum == 0) {
System.out.println("没有选择标签");
}
// 至少选择一个标签 && 不能选择一个标签 = ABCD之间自由组合
if ((leastOneCheckedRule & checkSum) == 0 && complexCheckRule.contains(checkSum)) {
System.out.println("标签之间没有自由组合");
}
最后一个校验稍微有点复杂,需要进行一下换算。
总结
位移运算在工作中很少使用到,以前只知道位移效率高,可以用来替代 乘法 和 除法。对位移运算的理解和应用也仅仅只限于熟悉运算规则并进行计算而已,很难想到上面这种通过位移来进行筛选。其实关于上面这些操作在离散数学中都有介绍,只能怪自己当初摸鱼了(囧)。。。
现在总结一下位移运算一些很有特点的操作:
- 交集:A & B
- 并集:A | B
- 差集: A & (~B)
最后,在网上发现一些关于位移运算技巧:位运算有什么奇技淫巧?