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中四個常見的位操作符:
- ~ – 按位取反
- & – 按位與
- ~ – 按位異或
- | – 按位或
簡單應用如下(簡單起見,只展示二進位制)
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位”這種功能是非常實用的。比如,你可以讀取足夠的資料來確定最長的霍夫曼編碼的長度,當你得到你剛剛讀取的霍夫曼編碼的實際長度之後,你就可以把指標往前移相應長度。一個這樣的類可以把位運算醜陋的一面劃分成一個眼熟的程式碼塊。
類似的,如果你追求速度的話,那你會意外的發現表查詢是如此強大。假如你有一個霍夫曼編碼以0開頭,並且其他的編碼長度均為3而且以1開頭,這意味著你需要一個可以容納8(2^3)個項的表格,你的表格可能是這樣的:
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
- 十進位制與二進位制互相轉換指南
- JS的二進位制操作JS
- java中二進位制、八進位制、十進位制、十六進位制的轉換Java
- JAVA 二進位制,八進位制,十六進位制,十進位制間進行相互轉換Java
- JS操作二進位制方法 - blobJS
- 檔案操作(二進位制拷貝)
- 二進位制與二進位制運算
- 進位制詳解:二進位制、八進位制和十六進位制
- MySQL 安裝指南 二進位制安裝MySql
- java二進位制運算十進位制(精確運算)Java
- JavaScript 二進位制、八進位制與十六進位制JavaScript
- (二進位制)
- 二進位制
- C#的二進位制檔案操作C#
- 十進位制——二 (八、十六 )進位制
- 10進位制轉8進位制(棧操作)
- 二進位制,八進位制,十進位制,十六進位制的相互轉換
- 【進位制轉換】二進位制、十六進位制、十進位制、八進位制對應關係
- java二進位制相關基礎Java
- 二進位制、十進位制與十六進位制相互轉化
- 二進位制,八進位制,十進位制,十六進位制之間的轉換
- Python 進位制互相轉換(二進位制、十進位制和十六進位制)Python
- 計算機基礎進位制轉換(二進位制、八進位制、十進位制、十六進位制)計算機
- 二進位制轉十進位制快速方法
- 什麼是二進位制?二進位制如何轉換?
- 04 二進位制
- 大話二進位制,八進位制,十進位制,十六進位制之間的轉換
- JavaScript十進位制轉換為二進位制JavaScript
- Oracle二進位制與十進位制轉換Oracle
- 十進位制轉二進位制推導(草稿)
- Java二進位制Class檔案格式解析Java
- [計算機基礎] 計算機進位制轉換:二進位制、八進位制、十進位制、十六進位制計算機
- 一看就懂二進位制、八進位制、十六進位制數轉換十進位制
- python進位制轉換(二進位制、十進位制和十六進位制)及注意事項Python
- Oracle中的二進位制、八進位制、十進位制、十六進位制相互轉換函式Oracle函式
- 進位制之間的轉換之“十六進位制 轉 十進位制 轉 二進位制 方案”
- Web 前端開發日誌(二):JavaScript 的二進位制操作Web前端JavaScript