每日一博 | 如何优雅地运用位运算实现产品需求?
原文地址:梁桂钊的博客
欢迎关注公众号:「服务端思维」。一群同频者,一起成长,一起精进,打破认知的局限性。
如何优雅地运用位运算实现产品需求?
在开始正文之前,我们先来说一下 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; }
总结一下,我们在数据库设计时,将多个标识字段合并成一个字段,并把这个字段改成字符串型方式保存,不仅消除相同字段的冗余,而且当增加新的渠道类别时,不需增加新的字段,但是字符串类型的字段在查询效率和存储空间上不如整型字段。因此,我们可以参考用“位”来解决这个问题。我们采取不同的位来分别表示不同类别的标识字段。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
想替代 C 的 Zig 语言成立了基金会
Zig 语言官方宣布成立 Zig 软件基金会(Zig Software Foundation,ZFS)。 Today, I am proud to announce the Zig Software Foundation, a 501(c)(3) not-for-profit corporation, dedicated to promoting, protecting, and advancing the Zig programming language, supporting and facilitating the growth of a diverse and international community of Zig programmers, and providing education and guidance to students, teaching the next generation of programmers to be competent, ethical, and to hold each other to high standards. 今天,我自豪...
- 下一篇
Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇
Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇 本文主要内容: 1:spring cloud整合Eureka总结 本文是由凯哥(凯哥Java:kagejava)发布的《spring cloud系列》教程的总第十篇: 本文是几个维度中的第一个维度:注册与发现维度配置中心管理之Eureka相关教程第七篇-Spring cloud整合Eureka总结篇。 一:spring cloud整合Eureka总结 我们来回顾下到目前为止我们所学的知识: 我们先学习了微服务架构的入门知识: 微服务的介绍;spring cloud是什么?简单一句话:spring cloud是分布式微服务一站式解决方案。里面集成了很多优秀的项目,让我们从以下几个维度很好的来管理、治理我们的微服务; 接着我们介绍了分布式体系常见的几个维度: 服务注册与发现;服务调用;服务熔断;负载均衡;服务降级一级服务消息队列。这几个维度来治理的。如下图: 接着我们介绍了在2020年升级之后,每个维度使用的技术。如下图: 具体详见《spring cloud系列教程第一篇-介绍》在这篇文章中凯哥做了详...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS6,CentOS7官方镜像安装Oracle11G
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- 设置Eclipse缩进为4个空格,增强代码规范
- Mario游戏-低调大师作品
- MySQL8.0.19开启GTID主从同步CentOS8