前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊一聊Android 中巧妙的位操作

聊一聊Android 中巧妙的位操作

作者头像
程序员徐公
发布2018-12-24 10:01:05
5470
发布2018-12-24 10:01:05
举报

前言

我们之前,在计算机当中,它是以二进制的形式来进行数的存储和加减乘除的。

讲解之前,我们先来了解一下基本的位操作

位操作

含义

具体含义

&

表示与

两位同时为 1,结果才为 1,否则为 0

"| "

表示或

两位中只要有一个为 1,结果为 1

^

表示异或

两位中数字不相同为 1,否则为 0

表示取法

为单目运算符,表示取反

<<

左移运算符

向左移动一位

>>

右移运算符

向右移动一位

与运算符 &

两位同时为“1”,结果才为“1”,否则为“0”。

代码语言:javascript
复制
0 & 0 = 0; 0 & 1 = 0; 1 & 0 = 0; 1 & 1 = 1

或运算符 |

两位中只要有一位为 1,结果就为 1

代码语言:javascript
复制
0 | 0 = 0; 0 | 1= 1; 1 | 0 = 1; 1 | 1 = 1

异或云算符 ^

两位中只要数字不相同,结果即为 1

代码语言:javascript
复制
0 ^ 0 = 0;1 ^ 0= 1;0 ^ 1 = 1; 1 ^ 1 = 0

取反运算符 ~

左移运算:

左移运算 左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0.比如:

右移运算:

对于 >> 运算符来说,右移:(正数左补0、负数左补1)

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

结果是 -2 ,为什么会是 -2 呢?下面我们来看一下

代码语言:javascript
复制
转换成2进制为1111 1111 1111 1111 1111 1111 1111 1101

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

对于 >>> 运算符

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

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

结果为 2147483646

代码语言:javascript
复制
1111 1111 1111 1111 1111 1111 1111 1101无符号右移,高位补0,

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

Android 中位运算符的应用

“|” 或运算符的应用

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

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

代码语言:javascript
复制
TextView tv = new TextView(context);
tv.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);

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

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

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

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

什么叫位上错开?

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

代码语言:javascript
复制
into LEFT =  0x0001; 
int  CENTER_VERTICAL = = 0x0002;

位上错开有什么好处呢?

  • 节省空间,避免不必要的属性出现和维护成本(难道你想一个状态用一个布尔值来维护么?)
  • 获取方便,编码简洁,位运算也更加高效

“&” 与运算符

判断是否含有某种状态

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

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

代码语言:javascript
复制
int gravity = tv.getGravity();
if ((gravity & Gravity.LEFT) == Gravity.LEFT) {

}
判断是否是奇数或者偶数
  • 只需判断最后一位是1还是0
  • 最后一位是1,说明是奇数。最后一位是0,说明是偶数
  • 因为只有2的0次方才是奇数值1,其他的2的k(k = 1,2,….)都是偶数
代码语言:javascript
复制
public boolean isOdd(int num) {
    return (num & 1) != 0;
}

异或运算符的应用

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

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

  • 任何数和自己异或结果为零。
  • 任何数和0做异或值不变

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

代码语言:javascript
复制
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

非运算符的运用

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

值里面去除某个状态

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

代码语言:javascript
复制
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

代码语言:javascript
复制
      1111 0101(二进制) –取反-> 0000 1010(二进制) –加1-> 0000 1011(二进制)

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

代码语言:javascript
复制
      0000 1011(二进制) –取反-> 0000 0100(二进制) –加1-> 1111 0101(二进制)
代码语言:javascript
复制
int SignReversal(int a)  
{  
    return ~a + 1;  
}  
取绝对值
代码语言:javascript
复制
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)也可以得到绝对值。所以可以对上面代码优化下:

代码语言:javascript
复制
int my_abs(int a)  
{  
    int i = a >> 31;  
    return ((a ^ i) - i);  
}  

左移与右移运算符的应用

用来判断某一位是 1
代码语言:javascript
复制
/**
 * 判断第 n 位是 否为 1,注意 n 从 0 开始
 * @param num
 * @param n
 * @return
 */
public static boolean nBitisOne(long num, int n) {
    if (num <= 0) {
        return false;
    }
    return (1 & (num >> n)) == 1;
}

小结

  1. " | " 运算符用来组合值
  2. " &" 运算符用来或者值的某个状态或者去除值的某一个状态
  3. 与非剔除值
  4. 非用来取反或者取绝对值

其实位操作符还有很多妙用,由于篇幅有限,这里不再一一展开描述,下一篇,准备讲解常见的位操作算法题,敬请期待。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年12月08日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 与运算符 &
      • 或运算符 |
        • 异或云算符 ^
          • 取反运算符 ~
            • 左移运算:
              • 右移运算:
              • Android 中位运算符的应用
                • “|” 或运算符的应用
                  • “&” 与运算符
                    • 判断是否含有某种状态
                    • 判断是否是奇数或者偶数
                  • 异或运算符的应用
                    • 非运算符的运用
                      • 值里面去除某个状态
                      • 变换符号
                      • 取绝对值
                    • 左移与右移运算符的应用
                      • 用来判断某一位是 1
                  • 小结
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档