1. 程式人生 > >Java位運算總結:位運算用途廣泛

Java位運算總結:位運算用途廣泛

前天幾天研究了下JDK的Collection介面,本來準備接著研究Map介面,可是一檢視HashMap類原始碼傻眼咯,到處是位運算實現,所以我覺得還是有必要先補補位運算知識,不然程式碼看起來有點費力。今天系統研究了下,現記錄如下。

       首先要明白一個概念,Java位運算是針對於整型資料型別的二進位制進行的移位操作。主要包括位與、位或、位非,有符號左移、有符號右移,無符號右移等等。需要注意一點的是,不存在無符號左移<<<運算子。根據位運算的概念規定,我們首先需要弄明白兩個問題,java有哪些資料型別是整型資料型別和各數字進位制之間轉換問題。Java整型資料型別有:byte、char、short、int、long。要把它們轉換成二進位制的原碼形式,必須明白他們各佔幾個位元組。我們都知道,一個位元組佔8位。
      資料型別                           所佔位數
      byte                                       8 
      boolean                                8
      short                                    16
      int                                         32 
      long                                      64 
      float                                      32 
      double                                  64 
      char                                     16

還需要明白一點的是:計算機表示數字正負不是用+ -加減號來表示,而是用最高位數字來表示,0表示正,1表示負

所以比如-4用二進位制原碼錶示就是1111 1111 1111 1111 1111 1111 1111 1100

下面根據例項一個一個的來說明各種位運算的運算規則:
位與&(真真為真 真假為假 假假為假)
4&6
0000 0000 0000 0000 0000 0000 0000 0100
0000 0000 0000 0000 0000 0000 0000 0110
0000 0000 0000 0000 0000 0000 0000 0100
結果:4

位或|(真真為真 真假為真 假假為假)
4|6
0000 0000 0000 0000 0000 0000 0000 0100
0000 0000 0000 0000 0000 0000 0000 0110
0000 0000 0000 0000 0000 0000 0000 0110
結果:6

位非~(取反碼)【注:Java中正數的最高位為0,負數最高位為1,即最高位決定正負符號】
~4
0000 0000 0000 0000 0000 0000 0000 0100
1111 1111 1111 1111 1111 1111 1111 1011

解碼:先取反碼,再補碼
0000 0000 0000 0000 0000 0000 0000 0100
0000 0000 0000 0000 0000 0000 0000 0101
結果:-5

位異或^(真真為假 真假為真 假假為假)
4^6
0000 0000 0000 0000 0000 0000 0000 0100
0000 0000 0000 0000 0000 0000 0000 0110
0000 0000 0000 0000 0000 0000 0000 0010
結果:2

有符號右移>>(若正數,高位補0,負數,高位補1)
-4>>2
1111 1111 1111 1111 1111 1111 1111 1100   原碼
1111 1111 1111 1111 1111 1111 1111 1111   右移,最左邊空出兩位按規則負數空位補1
0000 0000 0000 0000 0000 0000 0000 0000   解碼
0000 0000 0000 0000 0000 0000 0000 0001   補碼(補碼即最後一位+1)
結果:-1


有符號左移<<(若正數,高位補0,負數,高位補1)
-4<<2
1111 1111 1111 1111 1111 1111 1111 1100   原碼
1111 1111 1111 1111 1111 1111 1111 0000   左移,最右邊空出兩位補0
0000 0000 0000 0000 0000 0000 0000 1111   

解碼
0000 0000 0000 0000 0000 0000 0001 0000   補碼
結果:-16

無符號右移>>>(不論正負,高位均補0)
-4>>>2
1111 1111 1111 1111 1111 1111 1111 1100   原碼
0011 1111 1111 1111 1111 1111 1111 1111   右移(由於高位均補0,故>>>後的結果一定是正數)
結果:1073741823

由於資料型別所佔位元組是有限的,而位移的大小卻可以任意大小,所以可能存在位移後超過了該資料型別的表示範圍,於是有了這樣的規定:
如果為int資料型別,且位移位數大於32位,則首先把位移位數對32取模,不然位移超過總位數沒意義的。所以4>>32與4>>0是等價的。

如果為long型別,且位移位數大於64位,則首先把位移位數對64取模,若沒超過64位則不用對位數取模。

如果為byte、char、short,則會首先將他們擴充到32位,然後的規則就按照int型別來處理。

學到這裡,我想你也可能會問,位運算到底有什麼用途或者有哪些場景可以應用到它。因為位運算的運算效率比直接對數字進行加減乘除高很多,所以當出現以下情景且對運算效率要求較高時,可以考慮使用位運算。不過實際工作中,很少用到它,我也不知道為什麼很少有人用它,我想應該是它比較晦澀難懂,如果用它來進行一些運算,估計編寫的程式碼的可讀性會不強,畢竟我們寫的程式碼不僅僅留給自己一個人看。
1.  判斷int型變數a是奇數還是偶數    
     a&1  = 0 偶數 
     a&1 =  1 奇數 
2.  求平均值,比如有兩個int型別變數x、y,首先要求x+y的和,再除以2,但是有可能x+y的結果會超過int的最大表示範圍,所以位運算就派上用場啦。
      (x&y)+((x^y)>>1); 
3.  對於一個大於0的整數,判斷它是不是2的幾次方
    ((x&(x-1))==0)&&(x!=0); 
4.  比如有兩個int型別變數x、y,要求兩者數字交換,位運算的實現方法:效能絕對高效
    x ^= y; 
    y ^= x; 
    x ^= y; 
5. 求絕對值
    int abs( int x ) 
   { 
     int y ; 
     y = x >> 31 ; 
    return (x^y)-y ;        //or: (x+y)^y 
   }
6.  取模運算,採用位運算實現:
     a % (2^n) 等價於 a & (2^n - 1) 
7.  乘法運算   採用位運算實現
     a * (2^n) 等價於 a << n
8.   除法運算轉化成位運算
      a / (2^n) 等價於 a>> n 
9.   求相反數
      (~x+1) 
10  a % 2 等價於 a & 1 

等等

當然還有牛人使用位運算來實現許可權控制,加密技術,裡面的奧祕深不可測