写点什么

聊一聊 Android 中巧妙的位操作

用户头像
Android架构
关注
发布于: 6 小时前

右移一位为 1111 1111 1111 1111 1111 1111 1111 1110,显而易见此为-2 补码.


对于 >>> 运算符


无符号右移(正数左补 0 负数左补 1)


System.out.println(-3>>>1);


结果为 2147483646


1111 1111 1111 1111 1111 1111 1111 1101 无符号右移,高位补 0,


01111 1111 1111 1111 1111 1111 1111 1110,其为 2147483646 的原码.




Android 中位运算符的应用



“|” 或运算符的应用

或运算符可以用来组合多种值。


举个例子,在 Android 中,我们经常会看到这样的写法


TextView tv = new TextView(context);


tv.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);


即 TextView 垂直居中并且向左对齐。


如果不采用或运算符来写,采用布尔值来记录每一种状态,那每一次绘制 TextView 的时候,你得判断多少次,才能得出 TextView 的对其方向。


因为 TextView 的对齐方向有可能 是左上,左下,左中,右上,右下,右中,中上,中下,垂直居中 ----。


采用或运算符来组合多种值的时候,为了便于获取原来的状态,这里我们需要注意一下,采用位上错开的原则:


什么叫位上错开?


举个例子,假设 LEFT 的值为 0x0001,即第一位为 1,CENTER_VERTICAL 的值为 0x0002 ,即第二位为 1,与 LEFT 第二位的值不同,那这样就叫位上错开。


into LEFT = 0x0001;


int CENTER_VERTICAL = = 0x0002;


位上错开有什么好处呢?


  • 节省空间,避免不必要的属性出现和维护成本(难道你想一个状态用一个布尔值来维护么?)

  • 获取方便,编码简洁,位运算也更加高效

“&” 与运算符

判断是否含有某种状态

上面我们说到或运算符可以用来组合多种值,那我们如何判断组合后的值含有某种状态,其实很简单。


跟原来的某一状态进行与,若值与该状态相等,证明含有该状态


int gravity = tv.getGravity();


if ((gravity & Gravity.LEFT) == Gravity.LEFT) {


}

判断是否是奇数或者偶数

  • 只需判断最后一位是 1 还是 0

  • 最后一位是 1,说明是奇数。最后一位是 0,说明是偶数

  • 因为只有 2 的 0 次方才是奇数值 1,其他的 2 的 k(k = 1,2,….)都是偶数


public boolean isOdd(int num) {


return (num & 1) != 0;


}

异或运算符的应用

异或运算符,只要两位不相等,结果为 1, 否则为 0.


因此,我们容易得出这一样的结果


  • 任何数和自己异或结果为零。

  • 任何数和 0 做异或值不变


使用异或运算符实现值的交换


void Swap(int a, int b)


{


if (a != b)


{


a ^= b;


b ^= a;


a ^= b;


}


}


  • 第一步 a^=b 即 a=(a^b);

  • 第二步 b^=a 即 b=b(ab),由于运算满足交换律,b(ab)=bb^a。由于一个数和自己异或的结果为 0 并且任何数与 0 异或都会不变的,所以此时 b 被赋上了 a 的值。

  • 第三步 a^=b 就是 a=ab,由于前面二步可知 a=(ab),b=a,所以 a=ab 即 a=(ab)^a。故 a 会被赋上 b 的值。


再来个实例说明下以加深印象。int a = 13, b = 6;


a 的二进制为 13=8+4+1=1101(二进制)


b 的二进制为 6=4+2=110(二进制)


  • 第一步 a^=b a = 1101 ^ 110 = 1011;

  • 第二步 b^=a b = 110 ^ 1011 = 1101;即 b=13

  • 第三步 a^=b a = 1011 ^ 1101 = 110;即 a=6

非运算符的运用

非运算符的作用就是按位取反。

值里面去除某个状态

或许,你会有这样的一个疑问,如果我想剔除当前已经包含的一个值,需要怎么办?这时候就是“非”和“与”运算符联合使用的时候了,且看下面代码


int left = 0x001;


int right = 0x002;


int top = 0x008;


int state = left | right;


System.out.println("剔除 right 状态前 " + state);


state &= ~right;


System.out.println("剔除 right 状态后 " + state);


state &= ~top;


System.out.println("剔除不存在的 top 状态 " + state);


输出 log 如下


剔除 right 状态前 3


剔除 right 状态后 1


剔除不存在的 top 状态 1

变换符号

如对于-11 和 11,可以通过下面的变换方法将-11 变成 11


1111 0101(二进制) –取反-> 0000 1010(二进制) –加 1-> 0000 1011(二进制)


同样可以这样的将 11 变成-11


0000 1011(二进制) –取反-> 0000 0100(二进制) –加 1-> 1111 0101(二进制)


int SignReversal(int a)


{


return ~a + 1;


}

取绝对值

int my_abs(int a)


{


int i = a >> 31;


return i == 0 ? a : (~a + 1);


}


现在再分析下。对于任何数,与 0 异或都会保持不变,与-1 即 0xFFFFFFFF 异或就相当于取反。因此,a 与 i 异或后再减 i(因为 i 为 0 或-1,所以减 i 即是要么加 0 要么加 1)也可以得到绝对值。所以可以对上面代码优化下:


int my_abs(int a)


{


int i = a >> 31;


return ((a ^ i) - i);


}

左移与右移运算符的应用

用来判断某一位是 1

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
聊一聊 Android 中巧妙的位操作