精妙的位运算
位运算
与运算(and)
&表示,当两个相同位对应的数都是 1 的时候,该位获得的结果才是 1,否则为 0
例如说6 & 11,转换为二进制就是0110 & 1011,结果为0010。
在位运算中常常与mask进行提取对应位值的操作。或运算(or)
|表示,当两个相同位对应的数只要有一个数 1 的时候,该位获得的结果为 1,否则为 0
还是用 6 和 11 这两个数做例子,就是0110 | 1011 = 1111。在位运算中可以起到赋值的作用。非运算(not)
~表示将每一位按位取反(原来是 0 结果就是 1,原来是 1 结果就是 0)。~110 = 001
在对Int值进行非运算时,因为计算机中存储的是补码,此时的非运算对补码进行取反后,再还原为原码后就会变为奇怪的值。
为何~1等于-2,~0等于-1呢异或运算(xor)
^表示,当两个相同位对应的数字不同的时候为 1,否则为 0,可以用A ^ B == 0来判断两个数是否相等。例如说:0110 ^ 1011 = 1101。可以用于判断两值是否相同并且提取出改变的位左移(shl)
<<表示,a << b表示 a 左移 b 位,由于移位在末位多出来的未知数字补零。
在这里面可以等价为a * 2^b这个运算(针对十进制)。0001 << 1 = 0010相当于1 * 2^1 = 2右移(shr)
>>表示,a >> b表示将 a 右移 b 位,原本的末位进行右移后会被舍弃,左边的用原有标志位补充,正数补0,负数补1。同样的,右移在十进制里面也可以近似为a / (2^b)的形式,不过要对结果取整,也不一定准确,只能够说意思大概如此。无符号右移
>>>表示,不管正负标志位为 0 还是 1,将该数的二进制码整体右移,左边部分总是以 0 填充,右边部分舍弃。
运算优先级: ~ > << = >>= >>> > & > ^ > | > && > ||
Android源码中的应用
View
在View.java的源码中,各种View状态都是保存到类型为Int的mViewFlags字段中,每一位或多位代表一个属性的状态,例如static final int ENABLED_MASK = 0x00000020;代表了从二进制的右侧数第6位的值代表了当前是否为ENABLED状态。
mViewFlags共使用了32位来存储状态,源码中表示为0x00000000,每一个0都是4位。而0x00000010中1为使用该4位中的最后一位,相同的,2为第三位,4为第二位,8为第一位。
修改mViewFlags的值在setFlags(flag,mask)方法中:
1 | void setFlags(int flags, int mask) { |
其中,(mViewFlags & ~mask)是将mask所标志的所在位原有值去除,(flags & mask)提取出要赋值的新值,并通过或运算|赋值至mViewFlags。此时新旧两值通过异或^判断值是否发生了改变。如果不相等,当前changed中为1的位为本次修改的位。
举个例子:假设当前
mViewFlags = 0x00400010,此时,我们想要将当前View设置为disable状态,调用了setFlags(DISABLED,ENABLED_MASK),int DISABLED = 0x00000020,int ENABLED_MASK = 0x00000020,那么,当前的(mViewFlags & ~mask) = 0x00400010 & ~0x00000020 = 0x00400010 & 0xffffffdf = 0x00400010,而(flags & mask) = 0x00000020 & 0x00000020 = 0x00000020,最终mViewFlags = 0x00400010 | 0x00000020 = 0x00400030,最后changed计算出mViewFlags ^ old为0x00000020,也就是等于mask的值。
在setFlags的剩余代码中,可以看到其他的位运算
1 | if (((changed & FOCUSABLE) != 0){ // 等于0则说明对应位没有发生变化,不等于0则说明发生了变化 |
位掩码BitMask的作用
mask标记了某一组属性所使用的一个或多个位,在获取对应属性状态时可以对mask进行&运算获得对应的值,屏蔽其他位的影响。保证了一定的安全性、可靠性。
MeasureSpec
MeasureSpec是一个32位的int值,高2位为SpecMode,低30位为SpecSize
1 | public static class MeasureSpec { |
位运算的优缺点
优点
- 省去了创建过多的属性变量,将多个属性集中到一个
Int中进行管理 - 运算效率高,并且可以同时进行多个属性的赋值、修改操作。例如
setFlags(A|B,A_MASK|B_MASK)
缺点
不经常使用的话看起来太费劲,并不直观。