栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

位运算以及逻辑运算

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

位运算以及逻辑运算

二进制中的原码、反码、补码

要了解位移运算符我们就要先来了解 原码、反码、补码

有符号数:

对于有符号数而言,符号的正、负机器是无法识别的,但由于正、负 “恰好是两种截然不同的状态,如果用"0’表示“正”,用"1"表示“负",这样符号也被数字化了,并且规定将它放在有效数字的前面,即组成了有符号数。所以,在二进制中使用最高位(第一位)来表示符号,最高位是0,表示正数,最高位是1,表示负数。

10000000 00000000 00000000 01111100

对于有符号数而言的性质:
⑴ 二进制的最高位是符号位:0表示正数,1表示负数
(2) 正数的原码、反码、补码都一样
(3) 负数的反码=它的原码符号位不变,其他位取反(0→1 ;1→0 )
(4) 负数的补码=它的反码+1
(5) 0的反码、补码都是0
(6) 在计算机运算的时候,都是以补码的方式来运算的

有符号数运算案列

正数相加

正数相减

无符号数:

无符号数是针对二进制来讲的,无符号数的表数范围是非负数。全部二进制均代表数值(所有位都用于表示数的大小),没有符号位。即第一个"0"或"1"不表示正负

10000000 00000000 00000000 01111100

比如说我们 java 中 char 它就算是一个无符号数,它占16位,而我们的无符号数 是不能进行运算的。

<< 左移运算符 左移一位

左移一位后的数值经过计算可以发现刚好值位移前数值的两倍,等价于乘2操作,在很多情况下可以当做乘2使用,但是并不代表真正的乘2,在一些特殊情况下并不等价

左移18位

此时二进制表达首位为1,此时数值为-1058799616,同理,如果继续位移,左位移20位,则值为59768832又变成了正数

注意: 所以根据这个规则,如果任意一个十进制的数左位移32位,右边补位32个0,十进制岂不是都是0了?当然不是!
当int类型的数据进行左移的时候,当左移的位数大于等于32位的时候,位数会先求余数,然后再进行左移我们求得的余数位,也就是说如果真的左移32位的时候,会先进行位数求余数,即为左移32位相当于左移0位,所以左移33的值和左移一位1是一样的。

>>右移运算符(带符号的)

>>> 无符号右移运算符


总结: 正数的左移与右移,负数的无符号右移,就是相应的补码移位所得,在高位补0即可负数的右移,就是补码高位补1,然后按位取反加1即可

逻辑运算

掌握逻辑和运算的区别是︰将二进制数表示的信息作为四则运算的数值来处理就是算数,像图形那样,将数值处理为单纯的和1 的罗列就是逻辑
计算机能够处理的运算,大体可分为逻辑运算和算数运算,算数运算指的是加减乘除四则运算;逻辑运算指的是对二进制各个数位的0和1分别进行处理的运算,包括()、()、()和()四种。

名称运算符
逻辑非~
逻辑与&
逻辑或
逻辑异或^

逻辑非指的是将0变成1,1变成0的取反操作

  ~ 1111 = 0000

逻辑与指的是"两个都是1时,运算结果才是1,其他情况下是0"

	0000 0011 
  & 0000 0101 
-------------------
  = 0000 0001

逻辑或指的是"至少有一方是1时,运算结果为1,其他情况下运算结果都是0"

   0000 0011 
 | 0000 0101
-------------------
 = 0000 0111

逻辑异或指的是"其中一方是1,另一方是0时运算结果才是1,其他情况下是0"

   0000 0011 
 | 0000 0101
-------------------
 = 0000 0110

我们看完前面基本的运算过后,在java的底层实现中我们知道有很多地方都是用的位运算和逻辑运算,原因应该大家都知道就是效率高,在计算机中不管是加减乘除最后做的都是加法运算,我现在给大家上主菜,然大家来看一下这种运算的巧妙。

使用实例

HashMap大家都不陌生,我们看一下HashMap 是如何初始化容量的。

static final int tableSizeFor(int cap) {
    int n = cap - 1;//这是为了处理 cap 本身就是 2 的N次方的情况
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

就是上述代码我们可以看到用到了>>>( 无符号右移) 和 |(逻辑或) 进行运算。

我们假设我们去传一个参数7给HashMap,我们通过计算,来看它到底会给我们new一个多大容量的数组

我们可以通过上面的图片看出最终结果是8,相信你应该看出来,这5个公式会通过最高位的1,拿到2个1、4个1、8个1、16个1、32个1。当然,有多少个1,取决于我们的入参有多大,但我们肯定的是经过这5个计算,得到的值是一个低位全是1的值,最后返回的时候 +1,则会得到1个比n 大的 2 的N次方。

当然java底层的位运算和逻辑运算也不是一成不变的,他们也在不断寻找最优的计算方式。

优化了 hash 值的计算方式,1.8之前通过一顿操作,而1.8之后只是简单的让高16位参与了运算。

// JDK 1.7
static int hash(int h) {
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
// JDK 1.8
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

还有就是扩容时计算节点在新表的索引位置方式从1.8之前的 “h & (length-1)”改成1.8之后的“hash & oldCap”,性能可能提升不大,但设计更巧妙、更优雅。
其实h & (length-1)的本质就是h % length(取余操作)

其实java底层大量用到了逻辑运算与位运算,像 bitCount方法(计算一个数的二进制的1的个数),虽然我一直没看懂。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/462179.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号