天天看點

Java二進制操作

移位

位運算中大多數操作都是向左移位和向右移位。在Java中,這對應着<<和>>這兩個操作符,示例如下:

/* 00000001 << 1 = 00000010 */
1 << 1 == 2 

/* 00000001 << 3 = 00001000 */
1 << 3 == 8

/* 11111111 11111111 11111111 11110000 >> 4 = 11111111 11111111 11111111 11111111 */
0xFFFFFFF0 >> 4 == 0xFFFFFFFF 

/* 00001111 11111111 11111111 11111111 >> 4 = 00000000 11111111 11111111 11111111 */
0x0FFFFFFF >> 4 == 0x00FFFFFF      

注意:向右移位是有符号操作符。和許多語言一樣,Java使用最高位來表示數值的正負,負數的最高位永遠為1。一個以1開頭的二進制數移位後還将以1開頭,一個以0開頭的二進制樹移位後還将以0開頭。是以要小心:Java是可以在整數中進行位運算的。

你可以使用叫作“無符号右移”運算符的第三個操作符:>>> 來實作以“0”填充的移位,這種移位會忽略符号位并總是用“0”來填充。

/* 10000000 00000000 00000000 00000000 >>> 1 = 01000000 00000000 00000000 00000000 */
0x80000000 >>> 1 == 0x40000000

/* 10000000 00000000 00000000 00000000 >> 1 = 11000000 00000000 00000000 00000000 */
0x80000000 >> 1  == 0xC0000000      

最大的用途之一是迅速求2的幂。1向左移位1位是2,移2位是4,移3位是8…… 相似的,向右移1位相當于是把該數除以2。

另一個用途便是建立掩碼。位掩碼可用于屏蔽或者修改一個二進制數中的某些指定位,下一部分會進行詳細講解。假如我們想要建立一個

00001000的掩碼,代碼十分簡單:

int bitmask = 1 << 3;      

你可以使用位運算操作符來建立更複雜的掩碼,下一部分同樣會講解位運算操作符。

位運算操作符

以下是Java中四個常見的位操作符:

l ~ – 按位取反

l & – 按位與

l ~ – 按位異或

l | – 按位或

l 簡單應用如下(簡單起見,隻展示二進制)

1010 & 0101 == 0000
1100 & 0110 == 0100

1010 | 0101 == 1111
1100 | 0110 == 1110

~1111 == 0000
~0011 == 1100

1010 ^ 0101 == 1111
1100 ^ 0110 == 1010      

比如,你可以通過“或”運算,把一個二進制數上的指定位“設定”為1,并且不會影響到其他位。

10000001 | 00100000 = 10100001 /* 第五位設為1 */
10000001 | 1 << 5 = 10100001 /* 同樣作用 */
00000000 | 1 << 2 | 1 << 5 = 00100100      

如果你想要選擇性的把某位設為0,你可以讓數與一個全1但是某位為0的數相與。

01010101 & ~(1<<2) == 01010101 & 11111011 == 01010001      

關于位順序

假設最高位是在左邊:

10010110
^      ^
|      |------- 第 0 位
|
|-------------- 第 7 位      

注意,第0位的值是2^0,第一位是2^1,……,第7位的值是2^7。

使用ParseInt

在你的代碼裡操作二進制數字的便利方法是使用Integer.parseInt()方法。Integer.parseInt(“101″,2)代表着把二進制數101轉換為十進制數(5)。這意味着,利用這個方法你甚至可以在for循環裡使用二進制數字:

/* 從5到15的循環 */
for (int b = Integer.parseInt("0101",2); b <= Integer.parseInt("1111",2); b++) {
    /* 做些什麼 */
}      

位讀寫

建議:自己實作一個用來把二進制位(比特)轉換為流并讀寫的類,盡量不要使用Java的輸入輸出流,因為Java的流隻能按位元組操作。你會覺得“給我接下來的N個比特”和“把指針往前移M位”這種功能是非常實用的。比如,你可以讀取足夠的資料來确定最長的霍夫曼編碼的長度,當你得到你剛剛讀取的霍夫曼編碼的實際長度之後,你就可以把指針往前移相應長度。一個這樣的類可以把位運算醜陋的一面劃分成一個眼熟的代碼塊。

char code[8];
int codelen[8];

code[0] = 'a'; codelen[0] = 1;
code[1] = 'a'; codelen[1] = 1;
code[2] = 'a'; codelen[2] = 1;
code[3] = 'a'; codelen[3] = 1;
code[4] = 'b'; codelen[4] = 3;
code[5] = 'c'; codelen[5] = 3;
code[6] = 'd'; codelen[6] = 3;
code[7] = 'e'; codelen[7] = 3;