位运算符在我们平时开发中可能使用的不多,顶多乘以2除以2可能会用到,在平时看源码中经常能看到位运算符的使用,然后就是在LeetCode…..

1、移位运算符

移位运算符分类如下:

  • 左移运算符;
  • 右移运算符;
  • 无符号左移运算符;
  • 无符号右移运算符。

其中左移运算符和右移运算符都可以理解成有符号移位运算符,左移运算符和无符号左移运算符效果是一样的,但右移运算符和无符号右移运算符效果是不一样的。

  1. // 有符号左移1位
  2. a << 1;
  3. // 有符号右移1位
  4. a >> 1;
  5. // 无符号左移1位
  6. a <<< 1;
  7. // 无符号右移1位
  8. a >>> 1;

学习本节前,需要学习一下二进制相关知识,比如有符号无符号,补码反码等。

1.1 有符号移位运算符

1.1.1 左移运算符

  1. 左移运算符的效果:低位补0,高位可能由于符号位数字改变,会由原来的正数左移成负数。

举一个int类型的数字733183670为例,说明一下左移运算符的效果。

Java中int类型中数据占32bit位。

十进制的733183670数字,转换成二进制如下:
image.png
左移1位,相当于733183670 2 = 1466367340,如下:
image.png
左移8位,变成-1283541504,首位符号位由0变为1,因此在使用左移运算符时要注意变成负数的情况,如下:
image.png
根据这个规则,左移32位后,右边补上32个0值是不是就变成了十进制的0了?答案是NO,当int类型进行左移操作时,*左移位数大于等于32位操作时,会先求余(%)后再进行左移操作
。也就是说左移32位相当于不进行移位操作,左移40位相当于左移8位(40%32=8)。当long类型进行左移操作时,long类型在二进制中的体现是64位的,因此求余操作的基数也变成了64,也就是说左移64位相当于没有移位,左移72位相当于左移8位(72%64=8)。
以上都是介绍的int类型数据的左移操作,对于其他数据类型,左移操作如下:

  • 由于double,float在二进制中的表现比较特殊,因此不能来进行移位操作,编译报错;
  • 其它几种整形byte,short移位前会先转换为int类型(32位)再进行移位。

    1.1.2 右移运算符

    右移运算符的效果:高位补符号位数字,即正数补0,负数补1,低位丢弃,每一位依次右移。
    

    还是int型数字:733183670,二进制如下:
    image.png
    右移1位,变为366591835,即733183670 / 2 = 366591835,如下:
    image.png
    右移8位,变为2863998,如下:
    image.png
    上面介绍的是正数的有符号右移,负数的有符号右移如下:
    -733183670的二进制如下:
    image.png
    -733183670有符号右移,高位补1,如下:
    image.png

    1.2 无符号移位运算符

    1.2.1 无符号左移运算符

    无符号左移跟有符号左移的效果完全一样。
    

    1.2.2 无符号右移运算符

    无符号右移效果:不论正数负数,高位通通补0,低位依次右移丢弃。
    

    无符号右移和有符号右移的最大区别是:有符号右移,高位补符号位数字,即正数补0,负数补1;而无符号右移,不论正数负数,高位通通补0。
    以-733183670 >>> 8 = 13913217为例,如下图:
    image.png
    无符号右移,负数会变成正数。

    2、异或运算符

    按位异或运算符是⊕,运算符左右两侧是二进制数字,效果是对应位上的数字,相同为0,不同为1。
    按位异或有3条重要性质:

  • 对于任意整数 a,有 a ⊕ a = 0;

  • 对于任意整数 a,有 a ⊕ 0 = 0 ⊕ a = a;
  • 异或运算满足交换律,即 a ⊕ b = b ⊕ a。

拓展:设整型数组 nums 中出现一次的数字为 x ,出现两次的数字为 a, a, b, b, …,即:nums = [a, a, b, b, …, x],
若将 nums 中所有数字执行异或运算,留下的结果则为出现一次的数字 x,即:a ⊕ a ⊕ b ⊕ b ⊕…⊕ x = x。

参考

Java中的>>,>>>
Java中的移位运算符