写在前面
如果是看过jdk的源码的话,可能会经常看到移位操作,比如:
因为这种移位操作效率高,并且书写简单,所以应用的还是比较广泛的,本文一起来看下,希望在工作中能真正的在合适的场景中用起来!
1:有符号移位操作(算数移位)
知识点:
1:符号位不参与移位
2:移出的位直接丢弃
1.1:正数有符号→移操作>>
以正数10为例,其二进制是00000000 00000000 00000000 00001010
,执行>>1
如下:
00000000 00000000 00000000 00001010
>>1
00000000 00000000 00000000 00000101 0 丢弃移出最右侧的一个0
结果:00000000 00000000 00000000 00000101
测试:
package org.example;public class AAA {public static void main(String[] args) {int oldCapacity = 10;
// System.out.println(oldCapacity >> 1); // 5
// System.out.println(Integer.toString(oldCapacity >> 1, 2));System.out.println("---移位操作前---");System.out.println(showWithBinary(Integer.toString(oldCapacity, 2)));String s = Integer.toString(oldCapacity >> 1, 2);System.out.println("---移位操作后---");System.out.println(showWithBinary(s));}private static String showWithBinary(String s) {
// System.out.println("原始十进制数:" + s);int spanZeroNum = 32 - s.length();StringBuffer spanZeroSb = new StringBuffer();int oneEight = 0;while (spanZeroNum-- > 0) {spanZeroSb.append("0");if (++oneEight % 8 == 0) spanZeroSb.append(" ");}
// System.out.println(spanZeroSb.toString() + s);return "转换为二进制表示:" + spanZeroSb.toString() + s;}
}
运行:
---移位操作前---
转换为二进制表示:00000000 00000000 00000000 00001010
---移位操作后---
转换为二进制表示:00000000 00000000 00000000 00000101Process finished with exit code 0
1.2:负数有符号→移操作>>
负数:高位插入1
。
以负数-10为例,其二进制原码是10000000 00000000 00000000 00001010
(注意转换为补码,负数是以补码形式存储的),执行>>1
如下:
10000000 00000000 00000000 00001010
反码:
11111111 11111111 11111111 11110101
+1得到补码:
11111111 11111111 11111111 11110110
>>1(符号为不参与移位)
得到11
1111111 11111111 11111111 11110110,高亮的是高位插入的1:
111111111 11111111 11111111 11110110
右侧移出的直接丢弃,得到结果:
111111111 11111111 11111111 1111011
整理8 8 8 8形式:
11111111 11111111 11111111 11111011
想要看结果还需要转换成其原码:
11111111 11111111 11111111 11111011
-1
11111111 11111111 11111111 11111010
反码:
10000000 00000000 00000000 00000101 即-5
所以运算是通过补码进行的,但是肉眼想要得到实际的结果还需要转换为原码来查看。
测试:
package org.example;public class AAA {public static void main(String[] args) {int oldCapacity = -10;
// System.out.println(oldCapacity >> 1); // 5
// System.out.println(Integer.toString(oldCapacity >> 1, 2));System.out.println("---移位操作前---");System.out.println(showWithBinary(Integer.toString(oldCapacity, 2)));String s = Integer.toString(oldCapacity >> 1, 2);System.out.println("---移位操作后---");System.out.println(showWithBinary(s));}private static String showWithBinary(String s) {
// System.out.println("原始十进制数:" + s);String signBit = s.startsWith("-") ? "1" : "0";s = s.replace("-", "");int spanZeroNum = 32 - s.length();StringBuffer spanZeroSb = new StringBuffer();int oneEight = 0;while (spanZeroNum-- > 0) {if (oneEight == 0) spanZeroSb.append(signBit);else spanZeroSb.append("0");if (++oneEight % 8 == 0) spanZeroSb.append(" ");}
// System.out.println(spanZeroSb.toString() + s);return "转换为二进制表示:" + spanZeroSb.toString() + s;}
}
运行:
---移位操作前---
转换为二进制表示:10000000 00000000 00000000 00001010
---移位操作后---
转换为二进制表示:10000000 00000000 00000000 00000101Process finished with exit code 0
1.3:正数有符号←移操作<<
以正数10为例,其二进制是00000000 00000000 00000000 00001010
,执行<<1
如下:
00000000 00000000 00000000 00001010
>>1
00000000 00000000 00000000 00010100
结果:20
测试:
package org.example;public class AAA {public static void main(String[] args) {int oldCapacity = 10;
// System.out.println(oldCapacity >> 1); // 5
// System.out.println(Integer.toString(oldCapacity >> 1, 2));System.out.println("---移位操作前---");System.out.println(showWithBinary(Integer.toString(oldCapacity, 2)));String s = Integer.toString(oldCapacity << 1, 2);System.out.println("---移位操作后---");System.out.println(showWithBinary(s));}private static String showWithBinary(String s) {
// System.out.println("原始十进制数:" + s);String signBit = s.startsWith("-") ? "1" : "0";s = s.replace("-", "");int spanZeroNum = 32 - s.length();StringBuffer spanZeroSb = new StringBuffer();int oneEight = 0;while (spanZeroNum-- > 0) {if (oneEight == 0) spanZeroSb.append(signBit);else spanZeroSb.append("0");if (++oneEight % 8 == 0) spanZeroSb.append(" ");}
// System.out.println(spanZeroSb.toString() + s);return "转换为二进制表示:" + spanZeroSb.toString() + s;}
}
运行:
---移位操作前---
转换为二进制表示:00000000 00000000 00000000 00001010
---移位操作后---
转换为二进制表示:00000000 00000000 00000000 00010100Process finished with exit code 0
1.4:负数有符号←移操作<<
低位补0
。
以负数数-10为例,其二进制补码是11111111 11111111 11111111 11110110
,执行<<1
如下:
11111111 11111111 11111111 11110110
<<1
1111111 11111111 11111111 111101100
整理成8888形式:
11111111 11111111 11111111 11101100
转换为原码
先-1:
11111111 11111111 11111111 11101011
再取反:
10000000 00000000 00000000 00010100 即-20
测试:
package org.example;public class AAA {public static void main(String[] args) {int oldCapacity = -10;
// System.out.println(oldCapacity >> 1); // 5
// System.out.println(Integer.toString(oldCapacity >> 1, 2));System.out.println("---移位操作前---");System.out.println(showWithBinary(Integer.toString(oldCapacity, 2)));String s = Integer.toString(oldCapacity << 1, 2);System.out.println("---移位操作后---");System.out.println(showWithBinary(s));}private static String showWithBinary(String s) {
// System.out.println("原始十进制数:" + s);String signBit = s.startsWith("-") ? "1" : "0";s = s.replace("-", "");int spanZeroNum = 32 - s.length();StringBuffer spanZeroSb = new StringBuffer();int oneEight = 0;while (spanZeroNum-- > 0) {if (oneEight == 0) spanZeroSb.append(signBit);else spanZeroSb.append("0");if (++oneEight % 8 == 0) spanZeroSb.append(" ");}
// System.out.println(spanZeroSb.toString() + s);return "转换为二进制表示:" + spanZeroSb.toString() + s;}
}
运行:
---移位操作前---
转换为二进制表示:10000000 00000000 00000000 00001010
---移位操作后---
转换为二进制表示:10000000 00000000 00000000 00010100
Process finished with exit code 0
2:无符号移位操作(逻辑移位)
符号位也参与移位,移出的位直接丢弃。
2.1:正数无符号→移操作>>>
以正数10为例,其二进制是00000000 00000000 00000000 00001010
,执行>>>1
如下:
00000000 00000000 00000000 00001010
>>>1
00000000 00000000 00000000 00000101 0 丢弃移出最右侧的一个0
结果:00000000 00000000 00000000 00000101
测试:
package org.example;public class AAA {public static void main(String[] args) {int oldCapacity = 10;
// System.out.println(oldCapacity >> 1); // 5
// System.out.println(Integer.toString(oldCapacity >> 1, 2));System.out.println("---移位操作前---");System.out.println(showWithBinary(Integer.toString(oldCapacity, 2)));String s = Integer.toString(oldCapacity >>> 1, 2);System.out.println("---移位操作后---");System.out.println(showWithBinary(s));}private static String showWithBinary(String s) {
// System.out.println("原始十进制数:" + s);String signBit = s.startsWith("-") ? "1" : "0";s = s.replace("-", "");int spanZeroNum = 32 - s.length();StringBuffer spanZeroSb = new StringBuffer();int oneEight = 0;while (spanZeroNum-- > 0) {if (oneEight == 0) spanZeroSb.append(signBit);else spanZeroSb.append("0");if (++oneEight % 8 == 0) spanZeroSb.append(" ");}
// System.out.println(spanZeroSb.toString() + s);return "转换为二进制表示:" + spanZeroSb.toString() + s;}
}
运行:
---移位操作前---
转换为二进制表示:00000000 00000000 00000000 00001010
---移位操作后---
转换为二进制表示:00000000 00000000 00000000 00000101Process finished with exit code 0
因为是正数,所以效果和>>是一样的。
2.2:负数无符号→移操作>>>
以负数数-10为例,其二进制补码是11111111 11111111 11111111 11110110
,注意因为>>>会让符号位也参与移位,而高位补0,所以,负数>>>会变成正数,执行>>>1
如下:
11111111 11111111 11111111 11110110
>>>1
011111111 11111111 11111111 1111011
测试程序:
package org.example;public class AAA {public static void main(String[] args) {int oldCapacity = -10;
// System.out.println(oldCapacity >> 1); // 5
// System.out.println(Integer.toString(oldCapacity >> 1, 2));System.out.println("---移位操作前---");System.out.println(showWithBinary(Integer.toString(oldCapacity, 2)));System.out.println(oldCapacity >>> 1);String s = Integer.toString(oldCapacity >>> 1, 2);System.out.println("---移位操作后---");System.out.println(showWithBinary(s));}private static String showWithBinary(String s) {
// System.out.println("原始十进制数:" + s);String signBit = s.startsWith("-") ? "1" : "0";s = s.replace("-", "");int spanZeroNum = 32 - s.length();StringBuffer spanZeroSb = new StringBuffer();int oneEight = 0;while (spanZeroNum-- > 0) {if (oneEight == 0) spanZeroSb.append(signBit);else spanZeroSb.append("0");if (++oneEight % 8 == 0) spanZeroSb.append(" ");}
// System.out.println(spanZeroSb.toString() + s);return "转换为二进制表示:" + spanZeroSb.toString() + s;}
}
输出:
---移位操作前---
转换为二进制表示:10000000 00000000 00000000 00001010
2147483643
---移位操作后---
转换为二进制表示:01111111111111111111111111111011Process finished with exit code 0
这里要注意,常规思维上的符号位在这种移位操作下也被当作
3:需要注意的点
3.1:负数的空位补0还是补1
有符号右移,负数:高位补1。
负数有符号←移操作低位补0。
3.2:为啥没有<<<
不知道你注意到没有,没有所谓的无符号左移操作<<<
,这是因为,无符号左移会丢掉符号位
,你可能会说,补一个符号位不就完了吗,补不了,为什么?因为高位已经被来自低位的数据给占据了。所以<<<
没有任何意义,进一步所以就没有提供这种移位运算方式了。
写在后面
参考文章列表
JAVA基础之移位操作 。
为什么右移有「有无」符号之别,而左移却没有? 。