作为开发者,掌握位枚举的开发技能可以帮助我们加快业务需求开发,提高系统性能
什么是位枚举?其实理解和掌握位枚举并不复杂。下面我们以传统的枚举来进行对比说明,助你加深对位枚举的理解和应用。
假如我们系统中有一个表示兴趣爱好的枚举类EnumHobbyType。那么我们会有如下两种实现方式
1.传统枚举实现方式
传统形式的枚举类信息如下:
public enum EnumHobbyType {
DEFAULT(0, "无爱好"),
SING(1, "唱歌"),
DANCE(2, "跳舞"),
SWIMMING(3, "游泳"),
GAME(4, "打游戏"),
COOKING(5, "烹饪"),
;
EnumHobbyType(Integer code, String name) {
this.code = code;
this.name = name;
}
private Integer code;
private String name;
public Integer getCode() {
return code;
}
public String getName() {
return name;
}
}
很显然,当我们以传统形式的枚举去表示爱好时,那么对应的我们的数据库表中对应的字段存储的值形式会是下图这样的格式(在代码层面,保存前我们会以指定符号拼接hobby字段)
与保存时对应,当页面查询时,我们的系统去查询库表,在代码层面我们一般会进行如下处理,把hobby字段进行切割:
String[] hobbies= StringUtils.split(hobby, ",");
以上处理过程,保存前拼接为字符串,查询时再切割字符串为数组,实际上性能会存在损耗,后期扩展也有些麻烦。所以就该我们的主角:位枚举现身了。
2.位枚举实现方式
位枚举是指利用位运算实现的枚举类,枚举的code以2的幂数依次递增。这样的话我们就可以利用按位或运算、按位与运算去快速的处理和枚举相关的业务。说到位运算大家第一反应应该就是速度快了。具体怎么玩呢?如下:
位枚举的实现的hobby枚举类
/**
* 枚举项请以2的幂数递增,如: 1,2,4,8,16,32,64,128...
*/
public enum EnumHobbyType {
DEFAULT(0L, "无爱好"),
SING(1L, "唱歌"),
DANCE(2L, "跳舞"),
SWIMMING(4L, "游泳"),
GAME(8L, "打游戏"),
COOKING(16L, "烹饪"),
;
EnumHobbyType(Long code, String name) {
this.code = code;
this.name = name;
}
private Long code;
private String name;
public Long getCode() {
return code;
}
public String getName() {
return name;
}
public static boolean isHasFlag(final Integer flag, final EnumHobbyType hobbyType) {
if (Objects.isNull(flag) || Objects.isNull(hobbyType)) {
return false;
}
if (DEFAULT.getCode().equals(flag)) {
return false;
}
Long code = hobbyType.getCode();
//一个整数 按位与自身 = 自身值
return (code & flag) == code;
}
/**
* 传入爱好枚举的整数结果,和每一项枚举项code进行按位与运算 可判定爱好中是否有此爱好项
* @param flag
* @return
*/
public static List getHitFlags(final Integer flag) {
if (Objects.isNull(flag)) {
return Collections.emptyList();
}
if (DEFAULT.getCode().equals(flag)) {
return Collections.emptyList();
}
List hitFlags = new ArrayList<>();
for (EnumHobbyType value : values()) {
if (value.getCode().equals(DEFAULT.getCode())) {
continue;
}
Long code = value.getCode();
//传入给定整数和任意一个枚举项进行按位与运算
if ((code & flag) == code) {
//结果等于枚举项本身,说明flag中有此枚举项
hitFlags.add(value);
}
}
return hitFlags;
}
/**
* 把枚举项进行按位或运算 得到所有爱好的整数结果
* @param hobbyTypes
* @return
*/
public static Long calculateFlag(List hobbyTypes) {
if (CollectionUtils.isEmpty(hobbyTypes)) {
return DEFAULT.getCode();
}
Long value = 0L;
for (EnumHobbyType propertyFlag : hobbyTypes) {
//将爱好list值进行按位或运算
value |= propertyFlag.getCode();
}
return value;
}
}
位枚举的核心要点如下:
- 枚举项请以2的幂数递增,如: 1,2,4,8,16,32,64,128...
- 任意两数m和n,如果m和n都是2的幂数,那么m和n进行按位或运算的结果就等于m和n的字面值相加,什么意思呢?即m|n = m+n,如m=2,n=4,那么m|n = 6。此处等价于加法运算
- 任意两数m和n,如果m和n都是2的幂数,p是m和n按位或的结果,那么就有p&n=n,p&m=m,类似于减法运算
这时候我们系统怎么保存和查询hobby呢?用位枚举的方式就变得很简单方便了,比如张三的爱好有唱歌、打游戏、烹饪,那么保存时我们直接调用封装的方法calculateFlag
得到张三最终的爱好结果即可,唱歌|打游戏|烹饪=25(1|8|16=25,位运算速度极快),按位或运算结果图如下:
查询时怎么处理呢?很简单,从表中查询到hobby,比如此处hobby值为25,怎么知道25有哪些爱好组成呢?直接调用位枚举类中封装的方法getHitFlags
,可快算获取到hobby(25)对应的枚举项有:唱歌、打游戏、烹饪(25&唱歌=唱歌,25&打游戏=打游戏,25&烹饪=烹饪)。运算结果截图如下:
合理利用位运算可以提升我们代码处理速度,位运算的应用很多,位枚举作为一种应用场景,对我们开发有很好的帮助。可以多多动手实践下,好好理解,为你自己所用。
OK,咱们后面回聊儿~