移位
位運算中大多數操作都是向左移位和向右移位。在Java中,這對應著<<和>>這兩個操作符,示例如下:
1
2
3
4
5
6
7
8
|
/* 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”來填充。
1
2
3
4
|
/* 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的掩碼,代碼十分簡單:
1
|
int bitmask = 1 << 3 ; |
你可以使用位運算操作符來創建更復雜的掩碼,下一部分同樣會講解位運算操作符。
位運算操作符
以下是Java中四個常見的位操作符:
- ? ~ – 按位取反
- ? & – 按位與
- ? ~ – 按位異或
- ? | – 按位或
- ? 簡單應用如下(簡單起見,只展示二進制)
1
2
3
4
5
6
7
8
|
1010 & 0101 == 0000 1100 & 0110 == 0100 1010 | 0101 == 1111 1100 | 0110 == 1110 ~ 1111 == 0000 ~ 0011 == 1100 1010 ^ 0101 == 1111 1100 ^ 0110 == 1010 |
比如,你可以通過“或”運算,把一個二進制數上的指定位“設置”為1,并且不會影響到其他位。
1
2
3
|
10000001 | 00100000 = 10100001 /* 第五位設為1 */ 10000001 | 1 << 5 = 10100001 /* 同樣作用 */ 00000000 | 1 << 2 | 1 << 5 = 00100100 |
如果你想要選擇性的把某位設為0,你可以讓數與一個全1但是某位為0的數相與。
1
|
01010101 & ~( 1 << 2 ) == 01010101 & 11111011 == 01010001 |
關于位順序
假設最高位是在左邊:
1
2
3
4
5
|
10010110 ^ ^ | |------- 第 0 位 | |-------------- 第 7 位 |
注意,第0位的值是2^0,第一位是2^1,……,第7位的值是2^7。
使用ParseInt
在你的代碼里操作二進制數字的便利方法是使用Integer.parseInt()方法。Integer.parseInt(“101″,2)代表著把二進制數101轉換為十進制數(5)。這意味著,利用這個方法你甚至可以在for循環里使用二進制數字:
1
2
3
4
|
/* 從5到15的循環 */ for (int b = Integer.parseInt("0101",2); b <= Integer.parseInt("1111",2); b++) { /* 做些什么 */ } |
位讀寫
建議:自己實現一個用來把二進制位(比特)轉換為流并讀寫的類,盡量不要使用Java的輸入輸出流,因為Java的流只能按字節操作。你會覺得“給我接下來的N個比特”和“把指針往前移M位”這種功能是非常實用的。比如,你可以讀取足夠的數據來確定最長的霍夫曼編碼的長度,當你得到你剛剛讀取的霍夫曼編碼的實際長度之后,你就可以把指針往前移相應長度。一個這樣的類可以把位運算丑陋的一面劃分成一個眼熟的代碼塊。
類似的,如果你追求速度的話,那你會意外的發現表查找是如此強大。假如你有一個霍夫曼編碼以0開頭,并且其他的編碼長度均為3而且以1開頭,這意味著你需要一個可以容納8(2^3)個項的表格,你的表格可能是這樣的:
1
2
3
4
5
6
7
8
9
10
|
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 ; |
通過兩次查找,你就可以定位到你要找的字符,并且還可以知道下一個字符在前面多少位置。這可要比某些一遍遍的循環去查找全部字符要劃算的多,也更節省內存。
以上所述是小編給大家介紹的Java二進制操作(動力節點Java學院整理),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!