原文地址:梁桂钊的博客
博客地址:http://blog.720ui.com
欢送关注公众号:「服务端思维」。一群同频者,一起成长,一起精进,突破认知的局限性。
如何优雅地使用位运算实现产品需要?
在开始注释之前,咱们先来说一下 Linux 的零碎权限设计。在 Linux 零碎中,为了保障文件的平安,对文件所有者、同组用户、其余用户的拜访权限进行了别离治理。其中,文件所有者,即建设文件或目录的用户。同组用户,是所属组群中的所有用户。其余用户,指的是既不是文件所有者,也不是同组用户的其余用户。每个文件和目录都具备读取权限、写入权限和执行权限,这三个权限之间互相独立。
在 Linux 零碎中,每个文件的拜访权限能够用 9 个字母示意,每 3 个字母示意一类用户权限,别离代表文件创建者、同组用户、其余用户。其中,r 示意读取权限,w 示意写入权限,x 示意执行权限。通过性能模式批改文件权限,有三个局部组成,包含对象、操作和权限。
假如须要减少同组用户写入权限,上面来看一个例子。
chmod g+w /root/install.log
此外,每一类用户的拜访也能够通过数字的形式进行示意。
那么,通过数字模式就能够对常见的 Linux 文件权限操作进行演绎。
假如须要设置创建者可读可写可执行、同组用户可读、其余用户可读,咱们能够这样写:
chmod 755 /root/install.log
事实上,Linux 的文件拜访权限就是十分经典的位运算应用场景。独一无二,咱们再来看下 Java 中的 java.lang.reflect.Modifier
。其中,Modifier
类采纳 16 进制定义了动态常量。
public static final int PUBLIC = 0x00000001;
public static final int PRIVATE = 0x00000002;
public static final int PROTECTED = 0x00000004;
public static final int STATIC = 0x00000008;
public static final int FINAL = 0x00000010;
public static final int SYNCHRONIZED = 0x00000020;
public static final int VOLATILE = 0x00000040;
public static final int TRANSIENT = 0x00000080;
public static final int NATIVE = 0x00000100;
public static final int INTERFACE = 0x00000200;
public static final int ABSTRACT = 0x00000400;
public static final int STRICT = 0x00000800;
...
紧接着,Modifier
类提供了很多静态方法,例如 isPublic() 办法的返回值 & PUBLIC
对应的 16 进制值,如果非 0,则阐明含有 public 修饰符。
public static boolean isPublic(int mod) {return (mod & PUBLIC) != 0;
}
这里有一个重要的知识点,采纳 & 运算,两位同时为 1,后果才为 1,否则为 0。即 0&0=0; 0&1=0; 1&0=0; 1&1=1。例如:3&1 即 0000 0011 & 0000 0001 = 00000001,值为 1。
0000 0011
& 0000 0001
= 0000 0001
与此同时,Modifier
类还采纳 | 运算,确保加入运算的两个对象只有有一个为 1,其值为 1。即 0|0=0;0|1=1;1|0=1;1|1=1。例如 Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE | Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL | Modifier.STRICT 的后果是 3103,即 110000011111。
private static final int CLASS_MODIFIERS =
Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE |
Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL |
Modifier.STRICT;
0000 0000 0000 0001
| 0000 0000 0000 0010
| 0000 0000 0000 0100
| 0000 0000 0000 1000
| 0000 0000 0001 0000
| 0000 0100 0000 0000
| 0000 1000 0000 0000
= 0000 1100 0001 1111
书归正传,咱们站在前辈们的肩上,通过位运算设计优雅的多选标识,例如通过位运算实现权限管制或多状态治理,它的益处在于易扩大,防止数据库设计过程中字段收缩,缩小磁盘存储空间。
假如,咱们当初有一个有一个业务需要:在工作中增加一个告诉形式,可选项包含 IM 音讯、零碎揭示、邮箱、短信。抉择 IM 音讯后,反对 IM 即时发送;抉择零碎揭示后,反对站内信推送;抉择抉择邮箱后,该工作后续相干揭示内容,可通过发送邮件至相干人邮箱中进行告诉;抉择短信后,该工作后续相干揭示内容,可通过发送短信至相干人进行告诉。
咱们在设计数据库库表时,通常状况下,将多个标识字段合并成一个字段,并把这个字段改成字符串型形式保留,例如,存在 1 时示意反对 IM,2 时示意支持系统音讯,3 示意反对邮箱,4 示意反对短信。此时,如果同时都满足,它的存储模式就是以逗号分隔的字符串:“1,2,3,4”。这样设计的益处在于,不仅打消雷同字段的冗余,而且当减少新的渠道类别时,不需减少新的字段。
IM(1, "IM 音讯"),
SYSTEM(2, "零碎揭示"),
MAIL(3, "邮箱"),
SMS(4, "短信");
但在数据查问时,咱们须要对字符串进行分隔。并且字符串类型的字段在查问效率和存储空间上不如整型字段。因而,咱们能够用“位”来解决这个问题。咱们采取不同的位来别离示意不同类别的标识字段。
因而,当某个工作反对 IM 时,则保留 1(0000 0001);支持系统音讯时,则保留 2(0000 0010),反对邮箱时,则保留 4(0000 0100);反对短信时,则保留 8(0000 1000)。四种都反对,则保留 15(0000 11111)。
位 | 值 | 阐明 |
---|---|---|
00000001 | 1 | 反对 IM |
00000010 | 2 | 支持系统音讯 |
00000011 | 3 | 反对 IM、零碎音讯 |
00000100 | 4 | 反对邮箱 |
00000101 | 5 | 反对邮箱、IM |
00000110 | 6 | 反对邮箱、零碎音讯 |
00000111 | 7 | 反对邮箱、IM、零碎音讯 |
00001000 | 8 | 反对短信 |
… | ||
00001111 | 15 | 反对邮箱、IM、零碎音讯、短信 |
紧接着,咱们通过封装罕用办法来实现增删改。
/**
* 判断
* @param mod 用户以后值
* @param value 须要判断值
* @return 是否存在
*/
public static boolean hasMark(long mod, long value) {return (mod & value) == value;
}
/**
* 减少
* @param mod 已有值
* @param value 须要增加值
* @return 新的状态值
*/
public static long addMark(long mod, long value) {if (hasMark(mod, value)) {return mod;}
return (mod | value);
}
/**
* 删除
* @param mod 已有值
* @param value 须要删除值
* @return 新值
*/
public static long removeMark(long mod, long value) {if (!hasMark(mod, value)) {return mod;}
return mod ^ value;
}
总结一下,咱们在数据库设计时,将多个标识字段合并成一个字段,并把这个字段改成字符串型形式保留,不仅打消雷同字段的冗余,而且当减少新的渠道类别时,不需减少新的字段,然而字符串类型的字段在查问效率和存储空间上不如整型字段。因而,咱们能够参考用“位”来解决这个问题。咱们采取不同的位来别离示意不同类别的标识字段。
写在开端
【服务端思维】:咱们一起聊聊服务端核心技术,探讨一线互联网的我的项目架构与实战经验。让所有孤军奋战的研发人员都找到属于本人的圈子,一起交换、探讨。在这里,咱们能够认知降级,连贯顶级的技术大牛,连贯优良的思维形式,连贯解决问题的最短门路,连贯所有优良的办法,突破认知的局限。
更多精彩文章,尽在「服务端思维」!
本文由博客一文多发平台 OpenWrite 公布!