二进制及位运算应用

1:推算byte,int,long等类型的范围

公式:-2的n-1次方 ~ 2的n-1次方-1,n代表类型的位数

byte类型推算过程
1byte1字节,对应8位二进制数,最大值为:01111111
2:将二进制01111111转为10进制,计算公式:1*2^6 + 1*2^5 + 1*2^4 + 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0
3:去掉乘1简化公式:2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
4:将2^0替换成2^1 - 2^02^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^1 - 2^0
5:推导过程
	5.1:合并两个2^12^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^2 - 2^0
	5.2:合并两个2^22^6 + 2^5 + 2^4 + 2^3 + 2^3 - 2^0
	5.3:合并两个2^32^6 + 2^5 + 2^4 + 2^4 - 2^0
	5.4:合并两个2^42^6 + 2^5 + 2^5 - 2^0
	5.5:合并两个2^52^6 + 2^6 - 2^0
	5.6:合并两个2^62^7 - 2^0 = 2^7 - 1
6:推导结果:2^7 - 1,符合公式:2的n-1次方-1
7:负数推导类似,除了特殊值-128 = -2^7,对应的补码:10000000,减1计算反码,可以发现会算不下去
8:综上而论byte范围:-2^7 ~ 2^7 - 1

2:左移的效果(如何左移,可以查看这篇文章

公式:m * 2的n次方,m为原值,n为移动位数(前提是有效位(原码中的1)不能移动超过或等于符号位的位置,否则就会失效)

使用5进行推算
1int4字节,对应32位二进制数,5转化二进制:00000000 00000000 00000000 00000101
2:将 00000000 00000000 00000000 00000101 转为10进制,计算公式:2^2 + 2^0
3:左移1位:00000000 00000000 00000000 00001010 转为10进制,计算公式:2^3 + 2^1 = 2^1 *2^2 + 2^04:左移2位:00000000 00000000 00000000 00010100 转为10进制,计算公式:2^4 + 2^2 = 2^2 *2^2 + 2^05:左移29位:10100000 00000000 00000000 00000000 符号位变成1,已经变成负数
6:通过观察可以发现移动n位就相当于乘以2的n次方,但是前提是有效位不能移动超过或等于符号位的位置,否则就会失效

在HashMap中,数组初始长度和最大长度(移动31位,再移动就会变成负数)

在这里插入图片描述

3:右移的效果(如何右移,可以查看这篇文章

公式:m / 2的n次方,m为原值,n为移动位数

使用6进行推算
1int4字节,对应32位二进制数,6转化二进制:00000000 00000000 00000000 00000110
2:将 00000000 00000000 00000000 00000110 转为10进制,计算公式:2^2 + 2^1
3:右移1位:00000000 00000000 00000000 00000011 转为10进制,计算公式:2^1 + 2^0 =2^2 + 2^1/ 2^1
4:右移2位:00000000 00000000 00000000 00000001 转为10进制,计算公式:2^0 =2^2 + 2^1/ 2^2
5:右移3位:00000000 00000000 00000000 00000000 转为10进制,计算公式:0 =2^2 + 2^1/ 2^3
6:通过观察可以发现移动n位就相当于除以2的n次方,上面右移2位以上虽然有效位已经舍弃,但是整数除法结果不考虑浮点数,得到的结果是没有问题的

4:与运算的应用(如何与运算,可以查看这篇文章

快速取模:m % n = m & (n - 1),前提条件n等于2的某次方,且n必须为正数

使用10&3进行推算
110转化二进制:00000000 00000000 00000000 000010103转化二进制:00000000 00000000 00000000 00000011
2:计算过程:
		00000000 00000000 00000000 00001010
	&	00000000 00000000 00000000 00000011
	-----------------------------------------
		00000000 00000000 00000000 00000010
300000000 00000000 00000000 00000010 转为10进制,结果为2 = 10 % 4,符合公式
4:为什么说除数必须为2的某次方,因为对应的2进制,除了符号位,必定有且只有一位的值1,减1之后相当于把1后移1位,并且把1后面的位进行取反
	   例如:4 - 1 -> 00000000 00000000 00000000 00000100 - 1 = 00000000 00000000 00000000 00000011
														   &	01111111 11111111 11111111 11111111
														   -----------------------------------------
																00000000 00000000 00000000 00000011
																
	   例如:8 - 1 -> 00000000 00000000 00000000 00001000 - 1 = 00000000 00000000 00000000 00000111
															&	01111111 11111111 11111111 11111111
														   -----------------------------------------
																00000000 00000000 00000000 00000111
																
	   例如:16 - 1 -> 00000000 00000000 00000000 00010000 - 1 = 00000000 00000000 00000000 00001111
															&	 01111111 11111111 11111111 11111111
														   -----------------------------------------
																 00000000 00000000 00000000 00001111
																 
   这个时候可以看到只有低位连续为1,与运算之后,高位都是0,只有低位可能为1,那么结果一定是小于等于除数-1的值,刚好符合取模结果的特征	

在HashMap中,因为数组规定长度必须为2的n次方,所以使用取模计算数组下标的时候刚好可以使用与运算

在这里插入图片描述

5:异或运算的应用(如何异或运算,可以查看这篇文章

散列算法:HashMap中计算hash:(h = key.hashCode()) ^ (h >>> 16),得到的hash值就是为了上面后续与运算取模准备的,

1.先看看不对hashCode进行操作,后续的与运算会出现什么情况,因为HashMap默认数组长度为16,下面举几个例子进行:hashCode & (16 -1),看看结果如何
	例:hashCode = 131072 -> 00000000 00000010 00000000 00000000
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000000
	
	例:hashCode = 65536  -> 00000000 00000001 00000000 00000000
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000000	

	例:hashCode = 32768  -> 00000000 00000000 10000000 00000000
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000000							 
							 
	例:hashCode = 16384  -> 00000000 00000000 01000000 00000000
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000000
							 
2:通过上面例子可以发现,高16位的特征都已经丢失了,结果都是0,数据最终都落会到数组下标为0的位置,这是一个很明显的数据倾斜问题
		
3:这个时候对除数无符号右移16位,再处理上面例子	
	例:hashCode = 131072 -> 00000000 00000010 00000000 00000000
						-----------------------------------------
							 00000000 00000000 00000000 00000010				
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000010
	
	例:hashCode = 65536  -> 00000000 00000001 00000000 00000000
						-----------------------------------------
						     00000000 00000000 00000000 00000001
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000001	

	例:hashCode = 32768  -> 00000000 00000000 10000000 00000000
						-----------------------------------------
							 00000000 00000000 00000000 00000000
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000000							 							 
							 
	例:hashCode = 16384  -> 00000000 00000000 01000000 00000000
						-----------------------------------------						
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000000	
							 
4:这个时候可以发现数据没有那么倾斜了,但是低16位的特征丢失了,看3276816384,还是有一定倾斜,这个时候再对数据进行异或运算
	例:hashCode = 131072 -> 00000000 00000010 00000000 00000000
						-----------------------------------------
							 00000000 00000000 00000000 00000010
						^	 00000000 00000010 00000000 00000000		
						-----------------------------------------	
							 00000000 00000010 00000000 00000010	
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000010
	
	例:hashCode = 65536  -> 00000000 00000001 00000000 00000000
						-----------------------------------------
						     00000000 00000000 00000000 00000001
						^	 00000000 00000001 00000000 00000000	
						-----------------------------------------	
							 00000000 00000001 00000000 00000001							 
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000001	

	例:hashCode = 32768  -> 00000000 00000000 10000000 00000000
						-----------------------------------------
							 00000000 00000000 00000000 00000000
						^	 00000000 00000000 10000000 00000000	
						-----------------------------------------	
							 00000000 00000000 10000000 00000000							 
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000000							 
							 
	例:hashCode = 16384  -> 00000000 00000000 01000000 00000000
						-----------------------------------------
						     00000000 00000000 00000000 00000000
						^	 00000000 00000000 01000000 00000000	
						-----------------------------------------	
							 00000000 00000000 01000000 00000000						
						&	 00000000 00000000 00000000 00001111
						-----------------------------------------
							 00000000 00000000 00000000 00000000	 

5:这个时候可以看到虽然3276816384还是倾斜了,但是低16位的特征保留了,后续数组进行扩容上来,低16位就可以参与到运算了

6:为什么是要右移16位,而不是其他位数?个人看法是:数组长度绝大部分的情况是小于216次方,右移小于16位会丢失高位前几位
的特性(与运算这几位都会是0),如果大于16位又会丢失高位后面几位的特性,右移16位然后异或刚好可以保留到高16位和低16位的特性

7:为什么是使用异或而不是与运算,或运算?个人看法是:与运算位上面的结果会偏向0,因为只有都是1才会是1,并且高16位运算后都是0。
或运算的话,位上面结果会偏向1,因为只要一位是1结果就是1。异或可以使结果分散,减少数据的倾斜

8:为什么位移使用的是无符号右移,而不是直接右移?因为hashCode是存在负数的,如果是直接右移的话,进行异或操作的时候,数据的
特性将会被改变
	例:hashCode = -65536  -> 10000000 00000001 00000000 00000000
	                          11111111 11111110 11111111 11111111
	                          11111111 11111111 00000000 00000000
				    		-----------------------------------------
				    		  11111111 11111111 11111111 11111111
				    		^ 11111111 11111111 00000000 00000000	
				    		-----------------------------------------	
				    		  00000000 00000000 11111111 11111111

HashMap中进行操作的源码

在这里插入图片描述

使用异或进行数据交换

异或的特点
1:任何数异或自己,结果为0。因为自己异或自己,每一位的数都相同,相同的数异或的结果为0,所以最后结果会是0
	例:3 ^ 3 -> 00000000 00000000 00000000 00000011
			   ^ 00000000 00000000 00000000 00000011
			-----------------------------------------	
			     00000000 00000000 00000000 00000000
2:任何数异或0,结果都是自己。因为0异或1,结果为1,0异或0,结果还是0	
	例:3 ^ 0 -> 00000000 00000000 00000000 00000011
			   ^ 00000000 00000000 00000000 00000000
			-----------------------------------------	
			     00000000 00000000 00000000 00000011
3:使用异或进行数据交换
	例:a和b进行数据
		a = a ^ b
		b = a ^ b = a ^ b ^ b = a ^ 0 = a
		a = a ^ b = a ^ b ^ a = b ^ 0 = b
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值