本人只是 Android小菜一个,写技术文档只是为了总结本身在最近学习到的知识,历来不敢为人师,若是里面有些不正确的地方请你们尽情指出,谢谢!java
Java
位移运算符是Java中基本的位运算操做,可是平时工做中没有直接使用到,因此一直对其没有仔细研究过,对其实现也并不清楚。最近打算学习下HashMap
的实现原理,发现里面有些代码用到了位移操做,下面的代码用到了无符号右移,第一次看到这段代码的时候彻底不了解这个右移到底要达到什么效果。学习
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
复制代码
小菜打算正好借着这个机会,好好学习下Java中的位移操做,首先要明白位移运算只能应用在long,int,short,char,byte
这几种数据类型,并且当要应用在short,char和byte
时,首先会把他们拓展成int
再进行运算。Java的位移运算具体包含下面几种:spa
操做符 | 名称 | 含义 | 例子 |
---|---|---|---|
<< | 左移运算符 | 把操做数的二进制按位左移指定位数,并用零在右边进行补齐 | 60 << 2 |
>> | 右移运算符 | 把操做数的二进制按位右移指定位数,并用符号位在左边进行补齐 | 60 >> 2 |
>>> | 无符号右移运算符 | 把操做数的二进制按位右移指定位数,并用零在左边进行补齐 | 60 >>> 2 |
左移运算就是把操做数按位进行左移,左边的位将被移除,后边新增位用零进行补齐,下图演示了一个左移过程:code
在左移1位的过程当中,因为最左位表明是符号位,在左移的过程当中这个符号位丢失,直接使用原来左二位做为符号位,同时用零在右边补齐,最终致使的结果原来的正整型数变成了负整型数.cdn
能够用代码进行验证:blog
int source = 1740967785; // 二进制为:01100111110001010000111101101001
Log.i(TAG, Integer.toBinaryString(source) + " << 1 : " + Integer.toBinaryString(source << 1) + " : " + (source << 1));
Android_Test: 1100111110001010000111101101001 << 1 : 11001111100010100001111011010010 : -813031726
复制代码
能够看到因为在左移1位的过程当中致使符号位发生了变化,原来的正数变成了负数。细心的读者可能会发如今输出中,原数据并无输出咱们指望的32位二进制而是只有31位,这是由于其符号位是0,在输出过程当中被忽略了。文档
在左移2位的过程当中,和左移1位比较相似,也会引发符号位的变化,这里直接给出验证结果:string
int source = 1740967785; // 二进制为:01100111110001010000111101101001
Log.i(TAG, Integer.toBinaryString(source) + " << 2 : " + Integer.toBinaryString(source << 2) + " : " + (source << 2));
Android_Test: 1100111110001010000111101101001 << 2 : 10011111000101000011110110100100 : -1626063452
复制代码
在左移3位的过程当中,从上面的示例图能够看到,符号位没有发生变化,右边直接用零对齐,其验证结果以下:hash
Log.i(TAG, Integer.toBinaryString(source) + " << 3 : " + Integer.toBinaryString(source << 3) + " : " + (source << 3));
Android_Test: 1100111110001010000111101101001 << 3 : 111110001010000111101101001000 : 1042840392
复制代码
从上面的演示结果能够发现:在左移过程当中,数据的符号位有可能发生变化,最终的结果就是发生由正到负或者由负到正的转变,并非直观上认为的是原来的2倍。所谓的左移1位变为原来的2倍这个结论只适用了较小的正整数,由于它们在最初的几回左移过程当中不会发生符号位的变化。it
右移运算就是把操做数按位右移,右边的位将被移除,左边新增位用符号位补齐,下图演示了一个右移过程:
int source = 0b01100111110001010000111101101001;
Log.i(TAG, Integer.toBinaryString(source) + " >> 1 = " + Integer.toBinaryString(source >> 1) + " ; " + source + " >> 1 = " + (source >> 1));
Android_Test: 1100111110001010000111101101001 >> 1 = 110011111000101000011110110100 ; 1740967785 >> 1 = 870483892
复制代码
这段代码中操做数是正数,进行右移一位操做后,结果仍然是正数而且是原操做数的一半。
int source = 0b11100111110001010000111101101001;
Log.i(TAG, Integer.toBinaryString(source) + " >> 1 = " + Integer.toBinaryString(source >> 1) + " ; " + source + " >> 1 = " + (source >> 1));
Android_Test: 11100111110001010000111101101001 >> 1 = 11110011111000101000011110110100 ; -406515863 >> 1 = -203257932
复制代码
这段代码中操做数是负数,进行右移一位操做后,结果仍然是负数而且是原操做数的一半。
从上面的分析和代码验证状况看,右移过程当中,操做数始终保持符号不变,且每右移一位,就变为原操做数的一半。
无符号右移运算相比较右移运算更加简单,由于其在右移过程当中直接在左边用0补齐便可,无须考虑当前操做数的符号,下图演示了一个无符号右移过程:
int source = 0b01100111110001010000111101101001;
int source_2 = 0b11100111110001010000111101101001;
Log.i(TAG, Integer.toBinaryString(source) + " >>> 4 = " + Integer.toBinaryString(source >>> 4) + " ; " + source + " >>> 4 = " + (source >>> 4));
Log.i(TAG, Integer.toBinaryString(source_2) + " >>> 4 = " + Integer.toBinaryString(source_2 >>> 4) + " ; " + source_2 + " >>> 4 = " + (source_2 >>> 4));
Android_Test: 1100111110001010000111101101001 >>> 4 = 110011111000101000011110110 ; 1740967785 >>> 4 = 108810486
Android_Test: 11100111110001010000111101101001 >>> 4 = 1110011111000101000011110110 ; -406515863 >>> 4 = 243028214
复制代码
本文主要介绍了Java
中的位移运算符,包括“<< 左移运算符”,“>> 右移运算符”和“>>> 无符号右移运算符”,在使用过程当中要注意“补齐方式”和补齐后操做数的符号,简单总结以下:
操做符 | 补齐方式 | 结果符号 |
---|---|---|
<< | 右边用 0 补齐 | 和原操做数没有绝对关系,取决于左移后符号位。 |
>> | 左边有原符号位补齐 | 和原操做数有相同符号。 |
>>> | 左边用 0 补齐 | 和原操做数无关,一直为正数。 |