按位与&或|运算实现组合属性
Posted wkw1125
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了按位与&或|运算实现组合属性相关的知识,希望对你有一定的参考价值。
一直觉得位运算(与&、或|、非~、异或^、左移<<、右移>>)比较深奥,平时也很少接触到,但最近项目又遇到了与位运算相关的“组合属性”用法,有必要学习掌握了。
组合属性
我不知道“组合属性”正确叫法是什么,拿例子说吧。
例子一: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? 原来是十六进制0x1000000,二进制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.
参考
以上是关于按位与&或|运算实现组合属性的主要内容,如果未能解决你的问题,请参考以下文章