美文网首页
Java中位运算符的运用

Java中位运算符的运用

作者: NoBugException | 来源:发表于2019-11-26 17:01 被阅读0次

位运算符分为:按位与按位或按位异或左移右移,符号表示分别是:&|^<<>>,在Java或者Android中如果使用位运算符会提高程序的性能,因为位运算符在计算机中的计算速度是非常快的。

在学习位运算之前,首先需要理解补码的概念,在计算机中的数值都是以补码的形式存在的,当计算机中处理数据时,其实就是处理数值的补码

假如一个整数5,它的二进制表示是101,那么它的补码也是101,所以它在计算机中的表示是101

那么,补码该怎么去理解?

计算机中,没有原码反码的概念,之所以引出原码反码的概念,其实是为了简化补码的理解,纵所周知,数值分为无符号数和有符号数,可以理解为正数负数
正数的原码、反码、补码相同;
负数的补码等于原码的反码再加一;

举个例子:

  • 计算5的补码

5是正数,所以不需要计算,正数的原码、反码、补码完全一致;

  • 计算-5的补码

假设-5的数据类型是byte(一个字节)(八位二进制)

-5的原码:10000101(第一位代表符号位)
-5的反码:11111010(符号位不变,剩下的7位取反)
-5的补码:11111011(反码+1,末尾如果是1则逢二进一,如果越界则越界的高位直接舍弃)

所以,-5在计算机中的表示是11111011

11111011取反之后再加一,结果值和-5的原码一致,所以得出结论:补码的补码是原码

假设-5用变量a表示,那么~a等于多少呢?

a前面的破浪符号表示取反的意思,取反就是二进制0变成1,1变成0,包括符号位,a的原码是10000101,那么取反之后就是01111010? 这个答案是错误的,因为原码并不是计算机中的表示,我们需要将原码转成补码之后才能取反,a的补码是11111011,其反码是00000100,所以-5的反码为4。

下面开始举例,看看在实际运用中的艺术吧。

【计算数值的奇偶性】

假设有一个正整数n,要求判断n是奇数还是偶数?

  • 方法一:直接除以2

这个方法是最容易想到的方法,直接除以2就可以判断奇偶性。

如果`(n / 2)`的值为0,则为偶数;
如果`(n / 2)`的值为1,则为奇数;
  • 方法二:使用&运算符
如果`n & 1`的值为0,则为偶数;
如果`n & 1`的值为1,则为奇数;
  • 方法三:使用取模(取余)运算符%
如果`n % 2`的值为0,则为偶数;
如果`n % 2`的值为1,则为奇数;

运算速率比较:方法一 < 方法二 < 方法三

【乘法运算】

假设有一个数n,要求乘以2k

  • 方法一:使用*运算符

    n * 2k

  • 方法二:使用<<运算符
n << k

【除法运算】

假设有一个数n,要求除以2k

  • 方法一:使用/运算符

    n / 2k

  • 方法二:使用<<运算符
n << k

【交换两个数值】

假设有两个数a,b,要求交换这两个数值?

  • 方法一:

定义一个临时区域,int temp;

temp = a;//将a的值放入临时区域
a = b;//将b的值赋值给a
b = temp;//将临时区域的值赋值给b
  • 方法二:采用^运算符

异或(^)的特性:

  • 任意数和自身异或结果为0,0和任意数异或结果还是其本身
  • 符合结合律和交换律

根据异或的特性,可以轻松交换两个变量的值

a ^= b; //相当于 a = a ^ b
b ^= a; //相当于 b = a ^ b
a ^= b;//相当于 a = a ^ b

【用户权限判断】(或者状态叠加的情况)

假设有四种权限,分别是:那么如何去合理的表示用户只有一个权限或多个权限的情况,比如:

  • 我是张三:只有查询权限;
  • 我是李四:有查询、增加、修改权限;
  • 我是王五:有查询、增加、修改、删除权限;

现在讲,四个权限分别用1、2、4、8来表示,那么张三、李四、王五三人的权限用一个整数来表示:

  • 张三:1
  • 李四:1 + 2 +4 = 7
  • 王五:1 + 2 + 4 + 8 = 15;

那么,张三的权限为1,李四的权限为7,王五的权限为15。

那么,重点来了

判断张三是否有查询权限:1 & 1 = 1;
判断张三是否有删除权限:1 & 8 = 0;
判断李四是否有查询权限:7 & 1 = 1;
判断李四是否有增加权限:7 & 2 = 1;
判断李四是否有修改权限:7 & 4 = 1;
判断李四是否有删除权限:7 & 8 = 0;
判断王五是否有删除权限:15 & 8 = 1;
判断王五是否有查询权限:15 & 1 = 1;

1代表拥有权限,0代表没有权限。

【一道经典面试题】

有1000个一模一样的瓶子,其中有 999 瓶是普通的水,有一瓶是毒药。任何喝下毒药的生物都会在一星期之后死亡。现在,你只有 10 只小白鼠和一星期的时间,如何检验出哪个瓶子里有毒药?

这是一道算法题,考官问的是解决思路,通过二分查找来解决。

思路如下:

10只小白鼠,用二进制表示,第一位是第一个白鼠,第二位是第二个白鼠,第三位是第三个白鼠,依次类推...,其中0表示存活,1表示死亡,10只白鼠,代表210=1024种变化,1000个瓶子足够满足需求。

为了便于理解,我将这道题目简化一下,如下:

有8个一模一样的瓶子,有一瓶是毒药。任何喝下毒药的生物都会立即死亡。现在,你只有 3只小白鼠,如何检验出哪个瓶子里有毒药?

现在给8个瓶子标号

(1)第一个瓶子用二进制表示:000
(2)第二个瓶子用二进制表示:001
(3)第三个瓶子用二进制表示:010
(4)第四个瓶子用二进制表示:011
(5)第五个瓶子用二进制表示:100
(6)第六个瓶子用二进制表示:101
(7)第七个瓶子用二进制表示:110
(8)第八个瓶子用二进制表示:111

以上二进制只有三位,这三位从左到右分别代表三只小白鼠,所以:

5、6、7、8瓶水混合起来为第一个样本,给第一个小白鼠喝;
3、4、7、8瓶水混合起来为第二个样本,给第二个小白鼠喝;
2、4、6、8瓶水混合起来为第三个样本,给第三个小白鼠喝;

可能出现的情况有:

(1)如果第一只小白鼠死了,那么2、4、6、8瓶水必然有一瓶有毒;

第一只小白鼠死亡是已知条件,遍历这8个瓶子的编号,与001做&运算,如果结果为1则这瓶水有可能是毒药。

    int k = 1;//假设第一只小白鼠死亡,二进制表示为001

    for(int i=0;i<8;i++){//i为瓶子编号
        int n = i & k;
        if(n == 1){
            //表示当前瓶子可能含有毒药
        }
    }

(2)如果第二个小白鼠死了,那么3、4、7、8瓶水必然有一瓶有毒;

    int k = 2;//假设第二只小白鼠死亡,二进制表示为010

    for(int i=0;i<8;i++){//i为瓶子编号
        int n = i & k;
        if(n == 1){
            //表示当前瓶子可能含有毒药
        }
    }
}

(3)如果第三个小白鼠死了,那么5、6、7、8瓶水必然有一瓶有毒;

    int k = 4;//假设第二只小白鼠死亡,二进制表示为100

    for(int i=0;i<8;i++){//i为瓶子编号
        int n = i & k;
        if(n == 1){
            //表示当前瓶子可能含有毒药
        }
    }

(4)如果一个没死,那么第1瓶有毒;

【用户编号的类型判断】

在2n-1~2n-1范围内的任意两个整数&操作的结果都是两数最小的那个数,不在这个范围内的&操作都是0。(n>=1)

  • 当n=1时,范围是1~1
  • 当n=2时,范围是2~3
  • 当n=3时,范围是4~7
  • 当n=4时,范围是8~15
  • 当n=5时,范围是16~31
  • 当n=6时,范围是32~63
  • 当n=7时,范围是64~127
  • 当n=8时,范围是128~255
  • 当n=9时,范围是256~511
  • 当n=10时,范围是512~1023
  • 当n=11时,范围是1024~2047
  • 当n=12时,范围是2048~4095
  • 当n=13时,范围是4096~8191
  • 当n=14时,范围是8192~16383
  • 当n=15时,范围是16384~32767
  • 当n=16时,范围是32768~65535
  • 当n=17时,范围是65536~131071
  • 当n=18时,范围是131072~262143
  • 当n=19时,范围是262144~524287
  • 当n=20时,范围是524288~1048575
    依次类推...

我所知道的应用场景:
(1)一个算法不仅能生成账户的useid,还能给userid分类,比如:教授账户、老师账户、学生账户,分别对应n=18,n=19,n=20,也就是说教授userid取值范围是131072--262143,老师userid的取值范围是262144--524287,学生userid的取值范围是524288--1048575,这里假设是范围A,范围B和范围C,接下来是重点:
取同一个范围中的任意两个数做位运算&操作,计算出来的值总是两数中的最小值;
取不同范围中的任意两个数做位运算&操作,计算出来的值总是0;

由于计算机默认把0当做false,把大于0的数当做true,所以这个算法在此场景下的作用有以下几点:

1.可以作为userid的使用;
2.可以将userid分类,并且可以用&运算符求出两个userid是否是同类,当前userid是否是教授,当前userid是否是老师,当前userid是否是学生。

(2)可以用作状态分类,取该算法的任意几个范围,每个范围都有一系列不同的数字。
每个范围可以被当做一种状态,每个状态又分为好几种小状态,这种尝尽用该算法还是比较方便的。

[状态添加和移除]

现有以下几种状态(状态不能为0,且是2次幂)

    int STATUS_1 = 1 << 0;
    int STATUS_2 = 1 << 1;
    int STATUS_3 = 1 << 2;
    int STATUS_4 = 1 << 3;
    int STATUS_5 = 1 << 4;

使用按位或添加状态,如:

 flag |= STATUS_1
 flag |= STATUS_2
 flag |= STATUS_3
 flag |= STATUS_4

使用按位与和按位非移除状态,如:

flag &= ~STATUS_1
flag &= ~STATUS_2
flag &= ~STATUS_3
flag &= ~STATUS_4

判断是否有这个状态,如:

a = flag & STATUS_1

如果a > 0,则flag有STATUS_1这个状态(其实a = STATUS_1)
如果a=0,则flag没有STATUS_1这个状态

[本章完...]

相关文章

  • Java中位运算符的运用

    位运算符分为:按位与、按位或、按位异或、左移、右移,符号表示分别是:&、|、^、<<、>>,在Java或者Andr...

  • 算法整理(1) 位运算相关

    运算符 位运算符用来对二进制位进行操作,Java中提供了如下的位运算符:位运算符中,除 ~ 以外,其余均为二元运算...

  • Java的运算符

    Java有4大类运算符:算术运算、位运算、关系运算和逻辑运算。 运算符的优先级 模运算符 Java中,模运算符不止...

  • java中按位运算总结

    java运算符中基本运算符运用比较多,加减乘除,&,||等运算符结合基础变量,还有一种运算符平时使用不多但是在数据...

  • python基础(四)----运算符

    一.算术运算符(基本同Java) 二.比较运算符(基本同Java) 三.赋值运算符(基本同Java) 四.位运算符...

  • java基础知识

    title: java基础知识tags: [java基础知识] 位运算符 java种的运算符有 “&”,“|”,“...

  • 小白的Python之路(3)--运算符

    @(Python3) 内容:运算符 1. 位运算符 Python中的运算符和Java中基本差不多,这里主要介绍一下...

  • java中的运算符

    内容 算数运算符 赋值运算符 比较运算符 逻辑运算符 位运算符 位移运算符 三元运算符java中的运算符,有以下几...

  • Java基础之运算符(4)

    1、在Java中运算符可以分为算数运算符、赋值运算符、比较运算符、逻辑运算符和位运算符。 2、算数运算符:+(正号...

  • Java &、&&、|、||、^、<

    Java运算符大致分为逻辑运算符(&&,||,!)、算数运算符(+, -, *, / ,+=)、位运算符(^,|,...

网友评论

      本文标题:Java中位运算符的运用

      本文链接:https://www.haomeiwen.com/subject/nlxewctx.html