按位与&或|运算实现组合属性

一直觉得位运算(与&、或|、非~、异或^、左移<<、右移>>)比较深奥,平时也很少接触到,但最近项目又遇到了与位运算相关的“组合属性”用法,有必要学习掌握了。

组合属性

我不知道“组合属性”正确叫法是什么,拿例子说吧。

例子一:Android中的布局文件有这种用法:

<Button
    ...
    android:layout_gravity="bottom | center_horizontal"
    ...
/>

上述布局为按钮的layout_gravity属性指定了两个值“bottom”和“center_horizontal”,这两个属性同时生效,效果是按钮会位于父容器底部的水平方向中央。

例子二:android用于安装apk的系统api:(有更简单的例子,只是项目中遇到这个而已)

// @SystemApi
public abstract void installPackage(
    Uri packageURI, IPackageInstallObserver observer, int flags,
    String installerPackageName);

第三个int参数flags用于设置安装方式的组合值,可以是以下值的组合:

public static final int INSTALL_FORWARD_LOCK = 0x00000001;//受限应用
public static final int INSTALL_REPLACE_EXISTING = 0x00000002;//覆盖安装
public static final int INSTALL_ALLOW_TEST = 0x00000004;//允许测试包
public static final int INSTALL_EXTERNAL = 0x00000008;//安装在外部SD卡
public static final int INSTALL_INTERNAL = 0x00000010;//安装在内置SD卡
...

用法如:

int i = INSTALL_REPLACE_EXISTING | INSTALL_EXTERNAL;//覆盖安装,装于外部SD卡
mPackageManager.installPackage(uri, observer, i, null);

为什么一个int值就可以表示多个属性呢?这就要归功于位运算的巧妙实现了。

位运算

位运算介绍可见 按位与、或、异或等运算方法,简单明了例子也好。这里摘录组合属性使用的与&或|操作:

按位或 |

运算
参加运算的两个数,按二进制位进行“或”运算。同位上只要有一个为1,则结果值的该位为1,否则为0。
如,3 | 5 = 7 的运算过程如下:

3 : 0000 0011 
5 : 0000 0101 
--------------  |运算
7 : 0000 0111

作用:常用来对一个数据的某些位置为1。(类比:打开多个开关)
方法:找到一个数,对应X要置1的位,该数的对应位为1,其余位为零。此数与X相或可使X中的某些位置1。
例:将X=1010 0000的低4位置1 ,用 X | 0000 1111 = 1010 1111即可得到。

按位与 &

参加运算的两个数,按二进制位进行“与”运算。同位上同时为1时,结果值的该位为1,否则为0。
如,3 & 5 = 1 的运算过程如下:

3 : 0000 0011 
5 : 0000 0101
--------------  &运算 
1 : 0000 0001

作用:常用来判断一个数中指定位是否为1。(类比:检测某个开关是否打开)
方法:找一个数,对应X要取的位,该数的对应位为1,其余位为零,此数与X进行“与运算”可以得到X中的指定位。
例:设X=10101110, 取X的低4位,用 X & 0000 1111 = 0000 1110 即可得到;
如果要判断X的第三位是否为1,用 X & 0000 0100 = 0000 0100 ,即可知道第三位为1。

组合属性的实现

原理

组合属性的实现,就是巧妙地使用了按位或|、按位与&。
首先,将多个属性设置为不冲突的二进制数,如:

//   十进制    二进制       十六进制
int A = 1;  //0000 0001   0x0001
int B = 2;  //0000 0010   0x0002
int C = 4;  //0000 0100   0x0004
int D = 8;  //0000 1000   0x0008
int E =16;  //0001 0000   0x0010
//(复习一下,Java7中可以用0b表示二进制数,如int D = 0b00001000,更直观)

设置组合属性时,用按位或|运算为一个整数结果result,像是打开了多个开关。如:

int result = A | C;// A|C = 0001|0100 = 0101, 即 A|C = 1|4 = 5

读取组合属性时,将结果result与特定属性二进制数做按位与&运算,若结算结果与该属性相等,说明该属性开关被打开了。如:

if ((result & A) == A) {
    // result & A = 0101&0001 = 0001, 即 result&A = A
    // A属性生效
}

if ((result & B) == B) {
    // result & B = 0101&0010 = 0000 != 0010, 即 result&B != B
    // B属性不生效
}

if ((result & C) == C) {
    // result & C = 0101&0100 = 0100, 即 result&C = C
    // C属性生效
}
注意

位运算按二进制进行,虽然并不要求参与运算的数表示为二进制,但应用常量方式定义和使用组合属性,并注释二进制以便理解。
如,以上代码用十进制表示,等价为:

int result = 1 | 3;
if ((result & 1) == 1) {
    // A属性生效
}

虽然等价,但我认为这种魔数(magic number)更难以理解组合属性的原理。如我在公司代码中看到一段:

int i = 16777216;//w t f? 原来是十六进制0x‭1000000‬,‭二进制0001000000000000000000000000‬
i |= 2;
mPackageManager.installPackage(uri, observer, i, null);

DEMO

package com.kwws.demo;

public class CombineParam {
    private static final int A = 1;// 0000 0001
    private static final int B = 2;// 0000 0010
    private static final int C = 4;// 0000 0100
    private static final int D = 8;// 0000 1000
    private static final int E = 16;//0001 0000

    public static void main(String[] args) {
        int result = A | C | E;
        echo("result:%d(%s)", result, Integer.toBinaryString(result));

        if ((result & A) == A) {
            echo("A works.");
        }
        if ((result & B) == B) {
            echo("B works.");
        }
        if ((result & C) == C) {
            echo("C works.");
        }
        if ((result & D) == D) {
            echo("D works.");
        }
        if ((result & E) == E) {
            echo("E works.");
        }
    }

    private static void echo(String format, Object... args) {
        System.out.println(String.format(format, args));
    }

    private static void echo(String str) {
        System.out.println(str);
    }
}

结果:

result:21(10101)
A works.
C works.
E works.

参考

  1. 按位与、或、异或等运算方法
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KwCoding

谢了老板您讷~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值