Java位运算在实际项目中的运用(开关)

之前《Java里位操作总结》这篇文章有介绍Java位运算的基本操作,这篇将介绍下Java位运算在实际项目中的运用。

位运算的性能是非常好的,相比运算流程,计算机更喜欢这种纯粹的逻辑门和移动位置的运算,但位运算在平常的业务代码里并不太常见,因为它的可读性不太好,但是我们仍然可以利用位运算来解决一些实际项目里的问题。

比如用来表示开关的功能,比如需求里经常有这种字段:是否允许xx(0不允许,1允许),是否有yy权限(0没有,1有),是否存在zz(0不存在,1存在)

上面只是举例,类似这种只有两种取值状态的属性,如果当成数据库字段放进去的话,太过浪费,如果之后又有类似的字段,又得新增数据库字段,为了只有两种取值的字段,实在是不太值得。

这个时候何不用一个字段来表示这些字段呢?你可能已经猜到要怎么做了:

一个int型或者long型的字段,让它的每一个二进制位拥有特殊含义即可,然后按照位运算将其对应的位置上的数值变成0或1,那如何将某个数的二进制位第x位上的数值变成1或0呢?其实这在位图结构里经常用到,就是利用1这个特殊的值作位移运算后再与原值进行位运算,让我们看下这个过程:

把一个数的第2位的字符变成1,现在假设这个数初始化为0,int型,我们把它当成二进制展示出来:

现在如何把这个数的第二位变成1呢?目前是这样做的:

即原值跟1左移1位后的值作或运算,先来看看1 << 1的结果:

然后拿着上图的结果,跟原数(也就是0)进行或运算:

可以看到,原数的第二位已经被置为1了,它的十进制对应2,其它位的数置为1也大同小异,例如,现在让第6位也变成1只需要:

即拿着原值(现在为2)跟1左移5位后的数做或运算,这个流程如下:

看完了把某个位置的数值置为1,那如何把某位设置为0呢?我们现在把上图里的结果的第6位重新置回0,目前的做法为:

即拿着原值(经过上面几步的运算,现在值为32)跟1左移5位按位取反后的数做与运算,来看下这个流程:

经过上面的流程,就可以把原值的第6位变成0了。

那么我们知道了让一个数的二进制位的某位变成0或1的方法,那如何知道一个数的某位上究竟是0还是1呢?毕竟我们业务代码需要知道第几位代表什么意思并且获取到对应位置上的值。

假如我现在想知道十进制int型数34的第6位是0还是1,写法如下:

即让原值(34)右移5位后跟1做与运算,来看下这个流程:

由图可以看出,想要知道一个数的第几位是1还是0,只需要将其对应位置上的值“逼”到最后一位,然后跟1相与即可,如果对应位置上的值是0,那么与1相与后的结果一定为0,反之一定为1。

总结

到这里已经说完了为什么要用一个数表示那么多开关,以及如何给一个开关位设置对应的开关值,以及如何找到对应开关位的值,有了这些操作,我们再也不需要为这种只有0和1取值的字段新增数据库字段了,因为一个int型的数字,就可以表达32个开关属性(一个int是4个字节,一个字节有8位,那么int有32个01代码二进制,去掉int类型的最左边的一个01代码,这个01代码使用来表示正负号的,那么去掉这个01代码之后,还剩下31个01代码。那么这31个01代码就可以设置成31个开关,0表示false,1表示true),如果超了,还可以扩成64位的long型。

代码

有了上面的理论总结,下面就写个Java代码实现业务,业务逻辑是本系统商品数量或属性有变化时需要有选择的通知各大电商平台。

+2