Java 实例区分右移>>, 无符号右移>>>与<<左移

前言

最近一段时间在研究算法,不可避免的就会涉及到Java中对数字的各种左移与右移的运算,同时这部分内容在JDK源码中也涉及不少,尤其是List,Map的扩容机制都使用到了这部分,但是在查找相关资料的时候,发现很多博客写的太过于理论,并不利于理解,笔者希望能从代码上给大家提供一个新的理解思路。博客将会从正负数,int,long等几个角度去分别说明其中的区别。

Int型右移>>

首先看右移,Java中直接的右移是有符号的右移。因此正数和负数的右移结果是截然不同的,笔者会在代码前面放置1-1作为对照。我们选择22作为对照参数,因为22转换位2进制以后是10110,其中01分布比较均匀,便于观察。

public static void rightMove() {
		System.out.println("正整数对照:"+Integer.toBinaryString(1));
		System.out.println("正整数输出:"+Integer.toBinaryString(22));
		System.out.println("正整数移动:"+ Integer.toBinaryString(22>>1));
		
		System.out.println("--------正负号分割线--------");
		
		System.out.println("负整数对照:"+Integer.toBinaryString(-1));
		System.out.println("负整数输出:"+Integer.toBinaryString(-22));
		System.out.println("正整数移动:"+ Integer.toBinaryString(-22>>1));
}
输出:
正整数对照:1
正整数输出:10110
正整数移动:1011
正整数移动:101
--------正负号分割线--------
负整数对照:11111111111111111111111111111111
负整数输出:11111111111111111111111111101010
负整数移动:11111111111111111111111111110101
负整数移动:11111111111111111111111111111010

这里可以看到对于32位的int型来说,高位的0都会被忽略,因此对于正整数只能看到有真实值的部分,所以人为的恢复一下:

正整数对照:00000000000000000000000000000001
正整数输出:00000000000000000000000000010110
正整数移动:00000000000000000000000000001011	右移一位
正整数移动:00000000000000000000000000000101	右移两位
--------正负号分割线--------
负整数对照:11111111111111111111111111111111
负整数输出:11111111111111111111111111101010
负整数移动:11111111111111111111111111110101	右移一位
负整数移动:11111111111111111111111111111010	右移两位

横向对比很容易发现:>>是有符号右移,正数右移高位补0,负数右移高位补1。因此当使用>>对正数数进行右移时,32位后最终任何int型数字都会变为0;而当使用>>对负数进行右移时,32位后最终任何int型会变成321,也就是-1

Int型无符号右移>>>

无符号右移,也叫做逻辑右移。由于是无符号的,因此它的运算方式正数和负数就是一致的了,也就是正负数右移高位补0

public static void rightLogicMove() {
	System.out.println("正整数对照:"+Integer.toBinaryString(1));
	System.out.println("正整数输出:"+Integer.toBinaryString(22));
	System.out.println("正整数移动:"+ Integer.toBinaryString(22>>>1));
	System.out.println("正整数移动:"+ Integer.toBinaryString(22>>>2));
	
	System.out.println("--------正负号分割线--------");
	
	System.out.println("负整数对照:"+Integer.toBinaryString(-1));
	System.out.println("负整数输出:"+Integer.toBinaryString(-22));
	System.out.println("负整数移动:"+ Integer.toBinaryString(-22>>>1));
	System.out.println("负整数移动:"+ Integer.toBinaryString(-22>>>2));
}
输出:
正整数对照:1
正整数输出:10110
正整数移动:1011
正整数移动:101
--------正负号分割线--------
负整数对照:11111111111111111111111111111111
负整数输出:11111111111111111111111111101010
负整数移动:1111111111111111111111111110101
负整数移动:111111111111111111111111111010

移动以后我们发现,正数移动的结果基本没有变化,但是负数移动后,显示上缺位置了。这是因为无符号右移,高位一律补0,不会显示,因此也手动补全一下:

正整数对照:00000000000000000000000000000001
正整数输出:00000000000000000000000000010110
正整数移动:00000000000000000000000000001011	右移一位
正整数移动:00000000000000000000000000000101	右移两位
--------正负号分割线--------
负整数对照:11111111111111111111111111111111
负整数输出:11111111111111111111111111101010
负整数移动:01111111111111111111111111110101	右移一位
负整数移动:00111111111111111111111111111010	右移两位

这里就很明显能够看出来:>>>所谓的无符号右移,正负数右移高位补0。右移32位后最终无论正负整数int型数字都会变为0

Int型左移<<

左移,Java中的直接左移就是字面意义上的左移,无论是正负号都会在最右端(也就是最后面)补0

public static void leftMove() {
	System.out.println("正整数对照:"+Integer.toBinaryString(1));
	System.out.println("正整数输出:"+Integer.toBinaryString(22));
	System.out.println("正整数移动:"+ Integer.toBinaryString(22<<1));
	System.out.println("正整数移动:"+ Integer.toBinaryString(22<<2));
	
	System.out.println("--------正负号分割线--------");
	
	System.out.println("负整数对照:"+Integer.toBinaryString(-1));
	System.out.println("负整数输出:"+Integer.toBinaryString(-22));
	System.out.println("负整数移动:"+ Integer.toBinaryString(-22<<1));
	System.out.println("负整数移动:"+ Integer.toBinaryString(-22<<2));
}
输出:
正整数对照:1
正整数输出:10110
正整数移动:101100
正整数移动:1011000
--------正负号分割线--------
负整数对照:11111111111111111111111111111111
负整数输出:11111111111111111111111111101010
负整数移动:11111111111111111111111111010100
负整数移动:11111111111111111111111110101000

同样手动对齐一下:

正整数对照:00000000000000000000000000000001
正整数输出:00000000000000000000000000010110
正整数移动:00000000000000000000000000101100	左移一位
正整数移动:00000000000000000000000001011000 	左移两位
--------正负号分割线--------
负整数对照:11111111111111111111111111111111
负整数输出:11111111111111111111111111101010
负整数移动:11111111111111111111111111010100 	左移一位
负整数移动:11111111111111111111111110101000 	左移两位

可以看到,除了高位0或者1以外,左移操作基本上正负是没有区别的。因此可以得出结论:<<是有符号左移,正数和负数左移后低位补0,左移32次后任何int型数字最终都会编程0

Long型右移>>

Long型的右移基本上和int型没什么区别,都是正数右移高位补0,负数右移高位补1。只不过从32位变成了64位。

正整数对照:000000000000000000000000000000000000000000000000000000000000001
正整数输出:000000000000000000000000000000000000000000000000000000000010110
正整数移动:000000000000000000000000000000000000000000000000000000000001011
正整数移动:000000000000000000000000000000000000000000000000000000000000101
--------正负号分割线--------
负整数对照:1111111111111111111111111111111111111111111111111111111111111111
负整数输出:1111111111111111111111111111111111111111111111111111111111101010
负整数移动:1111111111111111111111111111111111111111111111111111111111110101
负整数移动:1111111111111111111111111111111111111111111111111111111111111010

Long型无符号右移>>>

从输出来看,long型和int型结果也是基本完全一样,也是负号变为高64位为符号位。但是移动后高位符号位为0了。基本上也是:无符号右移,正负数右移高位补0

正整数对照:1
正整数输出:10110
正整数移动:1011
正整数移动:101
--------正负号分割线--------
负整数对照:1111111111111111111111111111111111111111111111111111111111111111
负整数输出:1111111111111111111111111111111111111111111111111111111111101010
负整数移动:1111111111111111111111111110101
负整数移动:111111111111111111111111111010

由于是long型,补全来看,64位下由于是无符号高32位符号位全部都是0,只有低32位才是有效的,补全后如下:

正整数对照:00000000000000000000000000000000 00000000000000000000000000000001
正整数输出:00000000000000000000000000000000 00000000000000000000000000010110
正整数移动:00000000000000000000000000000000 00000000000000000000000000001011	
正整数移动:00000000000000000000000000000000 00000000000000000000000000000101	
--------正负号分割线--------
负整数对照:11111111111111111111111111111111 11111111111111111111111111111111
负整数输出:11111111111111111111111111111111 11111111111111111111111111101010
负整数移动:00000000000000000000000000000000 01111111111111111111111111110101	
负整数移动:00000000000000000000000000000000 00111111111111111111111111111010	

Long型左移<<

Long型左移和int型左移也相差不多。无非也是把64位直接左移,低位补0

正整数对照:1
正整数输出:10110
正整数移动:101100
正整数移动:1011000
--------正负号分割线--------
负整数对照:1111111111111111111111111111111111111111111111111111111111111111
负整数输出:1111111111111111111111111111111111111111111111111111111111101010
负整数移动:1111111111111111111111111111111111111111111111111111111111010100
负整数移动:1111111111111111111111111111111111111111111111111111111110101000

同样做一个long型的完整对比:

正整数对照:00000000000000000000000000000000 00000000000000000000000000000001
正整数输出:00000000000000000000000000000000 00000000000000000000000000010110
正整数移动:00000000000000000000000000000000 00000000000000000000000000101100
正整数移动:00000000000000000000000000000000 00000000000000000000000001011000
--------正负号分割线--------
负整数对照:11111111111111111111111111111111 11111111111111111111111111111111
负整数输出:11111111111111111111111111111111 11111111111111111111111111101010
负整数移动:11111111111111111111111111111111 11111111111111111111111111010100
负整数移动:11111111111111111111111111111111 11111111111111111111111110101000

最后

左移右移都说完了,那么<<<在哪呢?Java没有<<<这种操作,不用妄想了。

附:Long型的测试代码

public static void rightMoveLong() {
	System.out.println("正整数对照:"+Long.toBinaryString(1));
	System.out.println("正整数输出:"+Long.toBinaryString(22));
	System.out.println("正整数移动:"+ Long.toBinaryString(22>>1));
	System.out.println("正整数移动:"+ Long.toBinaryString(22>>2));
	
	System.out.println("--------正负号分割线--------");
	
	System.out.println("负整数对照:"+Long.toBinaryString(-1));
	System.out.println("负整数输出:"+Long.toBinaryString(-22));
	System.out.println("负整数移动:"+ Long.toBinaryString(-22>>1));
	System.out.println("负整数移动:"+ Long.toBinaryString(-22>>2));
}

public static void rightLogicMoveLong() {
	System.out.println("正整数对照:"+Long.toBinaryString(1));
	System.out.println("正整数输出:"+Long.toBinaryString(22));
	System.out.println("正整数移动:"+ Long.toBinaryString(22>>>1));
	System.out.println("正整数移动:"+ Long.toBinaryString(22>>>2));
	
	System.out.println("--------正负号分割线--------");
	
	System.out.println("负整数对照:"+Long.toBinaryString(-1));
	System.out.println("负整数输出:"+Long.toBinaryString(-22));
	System.out.println("负整数移动:"+ Long.toBinaryString(-22>>>1));
	System.out.println("负整数移动:"+ Long.toBinaryString(-22>>>2));
}

public static void leftMoveLong() {
	System.out.println("正整数对照:"+Long.toBinaryString(1));
	System.out.println("正整数输出:"+Long.toBinaryString(22));
	System.out.println("正整数移动:"+ Long.toBinaryString(22<<1));
	System.out.println("正整数移动:"+ Long.toBinaryString(22<<2));
	
	System.out.println("--------正负号分割线--------");
	
	System.out.println("负整数对照:"+Long.toBinaryString(-1));
	System.out.println("负整数输出:"+Long.toBinaryString(-22));
	System.out.println("负整数移动:"+ Long.toBinaryString(-22<<1));
	System.out.println("负整数移动:"+ Long.toBinaryString(-22<<2));
}
	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值