1.背景原因
假设我们有一批用户的权限信息,分为4类权限A,B,C,D。如何设计一种能够快速识别身份权限的数据结构呢?
首先我们先要对用户与权限建立一种映射关系,也就是 用户-权限信息。
我们可以采用Key-value的形式进行存储,由于我们主要是做权限认证,所以先调整个方向。 权限信息作为key, 用户表示作为value。
首先我们可以想到用HashMap来存储,HashMap<用户信息,权限信息>,接下来就是设置权限信息的格式了,我们可以先简单的定义用户信息 就是一个 long类型的id,权限信息就是一串字符串,由“权限标识“”和“_”组成,例如这样 “A_B” 这就代表有2中权限A,B.
但是这样有个问题,如果我们需要对某一类拥有指定权限的用户进行筛选就很麻烦。
假设现在有N个用户 M个方法
首先,我们先要遍历N个用户,然后对每一个用户的权限信息根据"_"字符进行拆分,然后在拆分的方法数组中进行遍历,从而找到特定用户。所耗费的次数为M * N。
可以明显的感觉到N其实是多余的。但是由于我们权限信息的数据格式设计的不合理,导致了我们需要进行这个N次比较。接下来我们就要想办法对权限信息的数据格式进行优化,使它1次就可以比较出是否含有指定权限。
一种紧凑的数据结构,就是位。 我们将权限信息用long这种类型来记录,而我们知道long一共有64位,那么我们就可以用它来表示64中状态。
2.解决与实现
一个简单的例子,
先定义权限信息
/**
* @author: TangFenQi
* @description: 定义一些权限枚举
* @date:2020/1/16 15:45
*/
public enum AuthEnum {
ADD(1 << 5),
UPDATE(1 << 4),
SELECT(1 << 3),
DELETED(1 << 2);
//权限值
@Getter
private long value;
AuthEnum(long value) {
this.value = value;
}
}
下面分别来展示 权限的增加,删除,查询,包含如何做。
用来保存用户与权限信息的关联关系
private Map<Long, Long> AUTH_MAP = new HashMap<>();
1.增加
private synchronized void add(long id, long addValue) {
if (!AUTH_MAP.containsKey(id)) {
AUTH_MAP.put(id, addValue);
} else {
long userValue = AUTH_MAP.get(id);
userValue = userValue | addValue;
AUTH_MAP.put(id, userValue);
}
}
只需要将值相或就行了。
2.删除
private synchronized void deleted(long id, long removeValue) {
long userValue = AUTH_MAP.get(id);
userValue = (removeValue & userValue) ^ userValue;
AUTH_MAP.put(id, userValue);
}
删除比较麻烦的一点就是,有可能传入的权限你本来就没有,所以不能直接异或,先要相与把不存在的权限位给归0,然后在异或将对应位置归0.
3.是否包含权限
private boolean contain(long id, long value) {
long userValue = AUTH_MAP.get(id);
long compareValue = userValue & value;
return compareValue == value;
}
进行相与,如果包含则其结果值就等于请求值。
4.获取用户权限
private List<AuthEnum> findAuth(long id) {
Long value = AUTH_MAP.get(id);
List<AuthEnum> authEnums = new ArrayList<>();
for (AuthEnum authEnum : AuthEnum.values()) {
if ((authEnum.getValue() & value) == authEnum.getValue()) {
authEnums.add(authEnum);
}
}
return authEnums;
}
我这定义了一个枚举,枚举里面的值就是权限的值。
3.总结
像这种紧凑的数据类型在实际的使用场景下还是有不错的发挥的,算是一种小技巧把。