Java 中的位移運算符 >> , << , >>>

最近在看一些在學習一些源碼,在源碼中常常會看到一些位移運算符的使用,好比在看HashMap源碼的時候,在計算hashMap容量的閾值的時候,就使用到了位移運算符,代碼以下:bash

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 二進制表達: ui

12345二進制表達.png

###一、左位移運算符 <<spa

若是 e << 1 ,左位移1位: code

左位移1位1.png

位移後十進制數值變成:24690,恰好是12345的二倍,因此有些人會用左位移運算符代替乘2的操做,可是這並不表明是真的就是乘以2,不少時候,咱們能夠這樣使用,可是必定要知道,位移運算符不少時候能夠代替乘2操做,可是這個並不表明二者是同樣的(這一點須要格外注意,不少人都存在這樣的誤解),接着往下看:cdn

若是繼續左移,12345左移14位: e<<14 blog

左位移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數學

二、右移運算符 >>

一樣,仍是以12345這個數值爲例, e右移1位: e>>1 hash

12345右移1位.png
右移後獲得的值爲 6172 和int 類型的數據12345除以2取整所得的值同樣,因此有些時候也會被用來替代除2操做

可是若是繼續右移,直接右移14位,即爲e>>14,則結果爲0 ,其過程和左移類似,就不一一演示了;另外,對於超過32位的位移,和左移運算符同樣,,會先進行位數求餘數

三、無符號右移運算符:>>>

無符號右移運算符和右移運算符是同樣的,不過無符號右移運算符在右移的時候是補0的,而右移運算符是補符號位的

一下是 -12345 二進制表達式

-12345二進制表達.png

對於源碼、反碼、補碼不熟悉的同窗,請自行學習,這裏就再也不進行補充了講解了,這裏提醒一下,在右移運算符中,右移後補0,是因爲正數 12345 符號位爲0 ,若是爲1 則應補1

無符號右移運算符.png

最後補充一下:

一、原碼、反碼和補碼

一個數能夠分紅符號位(0正1負)+ 真值,原碼是咱們正常想法寫出來的二進制。因爲計算機只能作加法,負數用單純的二進制原碼書寫會出錯,因而你們發明了反碼(正數不變,負數符號位不變,真值部分取反);再後來因爲+0, -0的爭端,因而改進反碼,變成補碼(正數不變,負數符號位不變,真值部分取反,而後+1)。二進制前面的0均可以省略,因此總結來講:計算機裏的負數都是用補碼(符號位1,真值部分取反+1)表示的。

二、位運算符和2的關係

位運算符和乘2 除2 在大多數時候是很類似的,能夠進行替代,同時效率也會高的多,可是二者切記不能混淆 ; 不少時候有人會把二者的概念混淆,尤爲是數據恰好是 二、四、六、八、100等偶數的時候,看起來就更類似了,可是對於奇數,如本文使用的12345 ,右移以後結果爲6172 ,這個結果就和數學意義上的除以2不一樣了,不過對於int 類型的數據,除2 會對結果進行取整,因此結果也是6172 ,這就更有迷惑性了

相關文章
相關標籤/搜索