精妙的位运算
位运算
与运算(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)
缺点
不经常使用的话看起来太费劲,并不直观。