在实际应用场景中 特别是 1,2,4,8,16 这种2的整数次幂的数字,因为具有非常典型的特点

  • 首先是 1、2、4 这几位数了,因为他们的特点就是二进制只有一个为 1 的位,其他位都是 0,并同其他数位 1 的位不冲突
  • 所以我们的其中一个场景 比如用户需要一个字段他是多个字段值组合而成的,比如这样一个场景 有一个员工技能表 有技能内容

  • 一个员工可能对应多个技能那我们通过

  • public static Integer addSkill(Integer originalSkill, Integer addSkill) {
    if (originalSkill == null) {
    return addSkill;
    }
    return originalSkill ^ addSkill;
    }

    如上代码 把多个技能相加 得到一个最终结果

  • 之后要展示时
  • 通过迭代 拿到每个子内容

首先来回顾一下位运算,什么是位运算呢?

位运算就是直接对整数在内存中的二进制位进行操作。

在 Java 语言中,位运算有如下这些:

  • 左移(<<)。

  • 右移(>>)。

  • 无符号右移(>>>)。

  • 与(&)。

  • 或(|)。

  • 非(~)。

  • 异或(^)。

在本篇文章中,我们所需要用到的有如下几个(其他的后续文章再讲):

  • &(与运算):只有当两方都为 true 时,结果才是 true,否则为 false。

  • |(或运算):只要当一方为 true 时,结果就是 true,否则为 false。

  • ^(异或运算):只要两方不同,结果就是 true,否则为 false。

以 true、false 为例:


true & true = true true & false = false true | false = true; false | false = false; true ^ true = false; true ^ false = true; 复制代码

以数字运算为例:


6 & 4 = ? 6 | 4 = ? 6 ^ 4 = ? 复制代码

当以数字运算时,我们首先需要知道这些数字的二进制,假设 6 是 int 类型,那么其二进制如下:


00000000 00000000 00000000 00000110 复制代码

在 Java 中,int 占了 4 个字节(Byte),一个字节呢又等于 8 个 Bit 位。所以 int 类型的二进制表现形式如上。

在这里为方便讲解,直接取后 8 位:00000110。

4 的二进制码如下:


00000100 复制代码

在二进制码中,1 为 true,0 为 false,根据这个,我们再来看看 6 & 4 的运算过程:


00000110 00000100 ----------- 00000100 复制代码

对每位的数进行运算后,结果为 4。

再来看看 | 运算:


6 | 4 = ? 复制代码

6 和 4 的二进制上面已经说了:


00000110 00000100 ----------- 00000110 复制代码

可以发现最后的结果是 6。

最后再来看看 ^ 运算:


6 ^ 4 = ? 复制代码

00000110 00000100 ----------- 00000010 复制代码

结果是 2。

应用

通过上面的例子,我们已经回顾了 & 、 | 以及 ^ 运算。现在来将它应用到实际的应用中。

假如我们现在要定义一个人的模型,这个人可能会包含有多种性格,比如说什么乐观型、内向型啦...

要是想要知道他包含了哪种性格,那么我们该如何判断呢?

可能在第一时间会想到:


if(这个人是乐观性){ .... }else if(这个人是内向型){ ... } 复制代码

那么如果有很多种性格呢?一堆判断写起来真的是很要命..

下面就来介绍一种更简单的方式。首先来定义一组数:


public static final int STATUS_NORMAL = 0;
public static final int STATUS_OPTIMISTIC = 1;
public static final int STATUS_OPEN = 2;
public static final int STATUS_CLOSE = 4;
复制代码

把它们转换为二进制:


0000 0000 0000 0000
0000 0000 0000 0001
0000 0000 0000 0010
0000 0000 0000 0100
复制代码

发现其中二进制的规律没有?都是 2 的次幂,并且二进制都只有一个为 1 位,其他都是 0 !

然后再来定义一个变量,用于存储状态(默认值是 0):


private static int mStatus = STATUS_NORMAL;
复制代码

当我们要保存状态时,直接用 | 运算即可:


mStatus |= STATUS_OPTIMISTIC;
复制代码

保存的运算过程如下:


00000000 执行 | 运算(只要有 1 则为 1) 00000001 ----------- 00000001 = 1 复制代码

相当于就把这个 1 存储到 0 的二进制当中了。

那么如果要判断 mStatus 中是否有某个状态呢?使用 & 运算:


System.out.println((mStatus & STATUS_OPTIMISTIC) != 0);// true,代表有它
复制代码

计算过程如下:


00000001 执行 & 运算(都为 1 才为 1) 00000001 ----------- 00000001 = 1 复制代码

再来判断一个不存在的状态 mStatus & STATUS_OPEN


System.out.println((mStatus & STATUS_OPEN) != 0);// false,代表没有它
复制代码

计算过程如下:


00000001 00000010 ----------- 00000000 = 0 复制代码

可以发现,因为 STATUS_OPEN 这个状态的二进制位,1 的位置处,mStatus 的二进制并没有对于的 1,而又因为其他位都是 0,导致全部归 0,计算出来的结果自然也就是 0 了。

这也就是为什么定义状态的数字中,是 1、2、4 这几位数了,因为他们的特定就是二进制只有一个为 1 的位,其他位都是 0,并同其他数位 1 的位不冲突。

如果换成其他的数,就会有问题了。比如说 3:


mStatus |= 3 复制代码

计算过程:


00000000 00000011 ----------- 00000011 = 3 复制代码

运算完毕,这时候 mStatus 中已经存储了 3 这个值了,我们再来判断下是否存在 2:


System.out.println((mStatus & 2) != 0);// true,代表有它,但是其实是没有的
复制代码

00000011 00000010 ----------- 00000010 = 2 复制代码

结果是 true,但是其实我们只存储了 3 到 mStatus 中,结果肯定是错误的。

所以我们在定义的时候,一定不要手滑定义错了数字。

存储和判断已经说了,那么如何取出呢?这时候就要用到 ^ 运算了。

假如现在 mStatus 中已经存储了 STATUS_OPTIMISTIC 状态了,要把它给取出来,这样写即可:


mStatus ^= STATUS_OPTIMISTIC 复制代码

其中的运算过程:


00000001 执行 ^ 运算,两边不相同,则为 true 00000001 ----------- 00000000 复制代码

可以看到状态又回到了最初没有存储 STATUS_OPTIMISTIC 状态的时候了。

最后再来看一个取出的例子,这次是先存储两个状态,然后再取出其中一个:


mStatus |= STATUS_OPTIMISTIC mStatus |= STATUS_OPEN 复制代码

存储完后,mStatus 的二进制为:


00000011 复制代码

再来取出 STATUS_OPEN 这个状态:


mStatus ^= STATUS_OPEN 复制代码

运算过程:


00000011 00000010 ----------- 00000001 复制代码

mStatus 现在就只有 STATUS_OPTIMISTIC 的状态了。

总结

通过 |、^、& 运算,我们可以很方便快捷的对状态值进行操作。当然,位运算的应用不仅限于状态值,知道了其中的二进制运算原理后,还有更多的其他应用场景,等着你去发现。

摘自
https://juejin.im/post/5d4cf36651882575595c449a

java位运算符生产环境应用分析的更多相关文章

  1. LeetCode刷题笔记(3)Java位运算符与使用按位异或(进制之间的转换)

    1.问题描述 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 算法应该具有线性时间复杂度并且不使用额外空间. 输入: [4,1,2,1,2] 输 ...

  2. Java 位运算符和 int 类型的实现

    Java 位运算符和 int 类型的实现 其他运算符 # 算术运算符 +.-.*./.++i.i++.--i.i-- # 关系运算符 ==.!=.>.<.>=.<= # 逻辑运 ...

  3. 利用Java位运算符,完成Unsigned转换(无符号)

    方案二:利用Java位运算符,完成Unsigned转换. 正常情况下,Java提供的数据类型是有符号signed类型的,可以通过位运算的方式得到它们相对应的无符号值,参见几个方法中的代码: publi ...

  4. 【java提高】(17)---Java 位运算符

    Java 位运算符 &.|.^.~.<<.>> 以前学过有关java的运算符,不过开发了这么久也很少用过这个.现在由于开发需要,所以现在再来回顾整理下有关java的运算 ...

  5. Java位运算符、位移运算符;原码、反码、补码

    文章背景:雪花算法 id 生成长度问题. Java位运算符 - 异或运算符(^)<p>运算规则:两个数转为二进制,然后从高位开始比较,如果相同则为0,不相同则为1.</p> - ...

  6. 【Java面试】生产环境服务器变慢,如何诊断处理?

    "生产环境服务器变慢?如何诊断处理" 这是最近一些工作5年以上的粉丝反馈给我的问题,他们去一线大厂面试,都被问到了这一类的问题. 今天给大家分享一下,面试过程中遇到这个问题,我们应 ...

  7. java 位运算符 以及加法 交换两个变量值

    先给出十转二的除法 2       60 30       0 15 0 7 1 3 1 1  1 0  1 60转二 111100 再介绍位运算符 a=60 b=13 A = 0011 1100 B ...

  8. Java位运算符浅析

    在学习源码中,发现有大量使用位运算符,这样做的目的是为了节约内存开销和加快计算效率. 位运算符,这个”位”代表这什么? 位:二进制位简称“位”,是二进制记数系统中表示小于2的整数的符号,一般用1或 0 ...

  9. Java位运算符&、|、^、>>、<<、~、>>>

    如果要搞懂Java中的位运算符,首先要搞懂二进制的运算,之前一篇有介绍详细请看 二进制运算-十进制与二进制的转换 Java中的位运算符有:&(按位与).|(按位或).^(按位异或).>& ...

随机推荐

  1. Java之选择排序(正序、逆序)

    public class SelectSort { public static void main(String[] args) { /** * @author JadeXu * @// TODO: ...

  2. pom文件中<dependencies>和<dependencyManagement>的区别

    在父pom中,如果使用了<dependencies>标签,那么在该标签体中的所有jar包,即使子工程中没有写这些依赖,依旧会引用. 如果使用了<dependencyManagemen ...

  3. 区块链学习1:Merkle树(默克尔树)和Merkle根

    ☞ ░ 前往老猿Python博文目录 ░ 一.简介 默克尔树(Merkle tree,MT)又翻译为梅克尔树,是一种哈希二叉树,树的根就是Merkle根. 关于Merkle树老猿推荐大家阅读<M ...

  4. Python怎么控制将一个整数输出成指定长的十六进制数?

    使用format方法,在格式控制中进行控制,具体控制参数为: {:#016X} 其中: 大括号表示该处从后面的format的参数中取值 冒号表示格式控制开始 0表示长度不足16位补0 16表示长度 X ...

  5. 转:【Python3网络爬虫开发实战】 requests基本用法

    1. 准备工作 在开始之前,请确保已经正确安装好了requests库.如果没有安装,可以参考1.2.1节安装. 2. 实例引入 urllib库中的urlopen()方法实际上是以GET方式请求网页,而 ...

  6. NOI2020网上同步赛 游记

    Day1 预计得分:\(32pts\)(我裂开了--) T1 美食家 表示考试的时候想到了关于矩阵快速幂的想法,甚至连分段后怎么处理都想好了,但是没有想到拆点,还有不知道怎么处理重边(这个考虑是多余的 ...

  7. 谷歌SameSite策略

    当我新下载谷歌80.x版本的时候,访问一个系统(内嵌跳转到其他系统的iframe),跳转过去没有携带cookie,以前版本的谷歌浏览器是可以的,控制台提示SameSite策略,给阻止了cookie携带 ...

  8. 基于menu小插件探索工程实践

    目录 一.准备工作 1.C/C++环境搭建 2.VSCode的配置 (1) 安装插件: (2) 设置配置文件: 二.工程化编程实战 1.模块化设计 2.可重用设计:进一步抽象 menu的进一步优化 可 ...

  9. Docker部署Portainer搭建轻量级可视化管理UI

    1. 简介   Portainer是一个轻量级的可视化的管理UI,其本身也是运行在Docker上的单个容器,提供用户更加简单的管理和监控宿主机上的Docker资源. 2. 安装Docker   Doc ...

  10. vue插值 v-cloak

    vue插值 v-cloak 使用VUE时,页面刷新时会出现闪动的现象(即在插值时会显示两侧的 {}) 先定义一个VUE 通过选择器在style中定义v-cloak的display值为none 再在元素 ...