Java 中的位移運算子 >> , << , >>>
最近在看一些在學習一些原始碼,在原始碼中經常會看到一些位移運算子的使用,比如在看HashMap原始碼的時候,在計算hashMap容量的閾值的時候,就使用到了位移運算子,程式碼如下:
static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
對位移運算子概念不熟悉的同學,看到這個估計是一臉懵逼,所以要想完全看懂原始碼,就需要對位移運算子有一定的瞭解。下面我用以任意一個10進位制的int 資料
int e = 12345 為例進行解析:
12345 二進位制表達:

12345二進位制表達.png
1、左位移運算子 <<
如果 e << 1 ,左位移1位:

左位移1位1.png
位移後十進位制數值變成:24690,剛好是12345的二倍,所以有些人會用左位移運算子代替乘2的操作,但是這並不代表是真的就是乘以2,很多時候,我們可以這樣使用,但是一定要知道,位移運算子很多時候可以代替乘2操作,但是這個並不代表兩者是一樣的(這一點需要格外注意,很多人都存在這樣的誤解),接著往下看:
如果繼續左移,12345左移14位:
e<<14

左位移14位.png
這裡要注意了,左位移18位後,二進位制首位為1,如下圖所示:

左位移18位.png
此時二進位制表達首位為1,此時數值為 -1058799616,同理,如果繼續位移,左位移20位,則值為 59768832 又變成了正數
所以根據這個規則,如果任意一個十進位制的數左位移32位,右邊補位32個0,十進位制豈不是都是0了?當然不是!!! 當int 型別的資料進行左移的時候,當左移的位數大於等於32位的時候,位數會先求餘數,然後再進行左移,也就是說,如果真的左移32位 e << 32 的時候,會先進行位數求餘數,即為 e<<(32%32) 相當於 e<< 0 ,所以e<< 33 的值和e<<1 是一樣的,都是 24690
2、右移運算子 >>
同樣,還是以12345這個數值為例,
e右移1位: e>>1

12345右移1位.png
右移後得到的值為 6172 和int 型別的資料12345除以2取整所得的值一樣,所以有些時候也會被用來替代除2操作
但是如果繼續右移,直接右移14位,即為e>>14,則結果為0 ,其過程和左移相似,就不一一演示了;另外,對於超過32位的位移,和左移運算子一樣,,會先進行位數求餘數
3、無符號右移運算子:>>>
無符號右移運算子和右移運算子是一樣的,不過無符號右移運算子在右移的時候是補0的,而右移運算子是補符號位的
一下是 -12345 二進位制表示式

-12345二進位制表達.png
對於原始碼、反碼、補碼不熟悉的同學,請自行學習,這裡就不再進行補充了講解了,這裡提醒一下,在右移運算子中,右移後補0,是由於正數 12345 符號位為0 ,如果為1 則應補1

無符號右移運算子.png
最後補充一下:
1、原碼、反碼和補碼
一個數可以分成符號位(0正1負)+ 真值,原碼是我們正常想法寫出來的二進位制。由於計算機只能做加法,負數用單純的二進位制原碼書寫會出錯,於是大家發明了反碼(正數不變,負數符號位不變,真值部分取反);再後來由於+0, -0的爭端,於是改進反碼,變成補碼(正數不變,負數符號位不變,真值部分取反,然後+1)。二進位制前面的0都可以省略,所以總結來說:計算機裡的負數都是用補碼(符號位1,真值部分取反+1)表示的。
2、位運算子和2的關係
位運算子和乘2 除2 在大多數時候是很相似的,可以進行替代,同時效率也會高的多,但是兩者切記不能混淆 ;
很多時候有人會把兩者的概念混淆,尤其是資料剛好是 2、4、6、8、100等偶數的時候,看起來就更相似了,但是對於奇數,如本文使用的12345 ,右移之後結果為6172 ,這個結果就和數學意義上的除以2不同了,不過對於int 型別的資料,除2 會對結果進行取整,所以結果也是6172 ,這就更有迷惑性了