Java位操作全面總結
在計算機中所有資料都是以二進位制的形式儲存的。位運算其實就是直接對在記憶體中的二進位制資料進行操作,因此處理資料的速度非常快。在實際程式設計中,如果能巧妙運用位操作,完全可以達到四兩撥千斤的效果,正因為位操作的這些優點,所以位操作在各大IT公司的筆試面試中一直是個熱點問題。
位操作基礎
基本的位操作符有與、或、異或、取反、左移、右移這6種,它們的運算規則如下所示:
注意以下幾點:
- 在這6種操作符,只有~取反是單目操作符,其它5種都是雙目操作符。
- 位操作只能用於整形資料,對float和double型別進行位操作會被編譯器報錯。
- 位操作符的運算優先順序比較低,因為儘量使用括號來確保運算順序,否則很可能會得到莫明其妙的結果。比如要得到像1,3,5,9這些2^i+1的數字。寫成int a = 1 « i + 1;是不對的,程式會先執行i + 1,再執行左移操作。應該寫成int a = (1 « i) + 1;
- 另外位操作還有一些複合操作符,如&=、|=、 ^=、«=、»=。
package com.king.bit; /** * @author taomk * @version 1.0 * @since 15-5-10 下午2:23 */ public class BitMain { public static void main(String [] args) { int a = -15, b = 15; System.out.println(a >> 2); // -4:-15 = 1111 0001(二進位制),右移二位,最高位由符號位填充將得到1111 1100即-4 System.out.println(b >> 2); // 3:15=0000 1111(二進位制),右移二位,最高位由符號位填充將得到0000 0011即3 } }
常用位操作小技巧
下面對位操作的一些常見應用作個總結,有判斷奇偶、交換兩數、變換符號及求絕對值。這些小技巧應用易記,應當熟練掌握。
判斷奇偶
只要根據最未位是0還是1來決定,為0就是偶數,為1就是奇數。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)來判斷a是不是偶數。下面程式將輸出0到100之間的所有偶數:
for (int i = 0; i < 100; i ++) {
if ((i & 1) == 0) { // 偶數
System.out.println(i);
}
}
交換兩數
int c = 1, d = 2; c ^= d; d ^= c; c ^= d; System.out.println("c=" + c); System.out.println("d=" + d);
可以這樣理解:
第一步 a=b 即a=(ab);
第二步 b=a 即b=b(ab),由於運算滿足交換律,b(ab)=bba。由於一個數和自己異或的結果為0並且任何數與0異或都會不變的,所以此時b被賦上了a的值;
第三步 a=b 就是a=ab,由於前面二步可知a=(ab),b=a,所以a=ab即a=(ab)a。故a會被賦上b的值;
變換符號
變換符號就是正數變成負數,負數變成正數。
如對於-11和11,可以通過下面的變換方法將-11變成11
1111 0101(二進位制) –取反-> 0000 1010(二進位制) –加1-> 0000 1011(二進位制)
同樣可以這樣的將11變成-11
0000 1011(二進位制) –取反-> 0000 0100(二進位制) –加1-> 1111 0101(二進位制)
因此變換符號只需要取反後加1即可。完整程式碼如下:
int a = -15, b = 15;
System.out.println(~a + 1);
System.out.println(~b + 1);
求絕對值
位操作也可以用來求絕對值,對於負數可以通過對其取反後加1來得到正數。對-6可以這樣:
1111 1010(二進位制) –取反->0000 0101(二進位制) -加1-> 0000 0110(二進位制)
來得到6。
因此先移位來取符號位,int i = a » 31;要注意如果a為正數,i等於0,為負數,i等於-1。然後對i進行判斷——如果i等於0,直接返回。否之,返回~a+1。完整程式碼如下:
int i = a >> 31;
System.out.println(i == 0 ? a : (~a + 1));
現在再分析下。對於任何數,與0異或都會保持不變,與-1即0xFFFFFFFF異或就相當於取反。因此,a與i異或後再減i(因為i為0或-1,所以減i即是要麼加0要麼加1)也可以得到絕對值。所以可以對上面程式碼優化下:
int j = a >> 31;
System.out.println((a ^ j) - j);
注意這種方法沒用任何判斷表示式,而且有些筆面試題就要求這樣做,因此建議讀者記住該方法(_講解過後應該是比較好記了)。
位操作與空間壓縮
篩素數法在這裡不就詳細介紹了,本文著重對篩素數法所使用的素數表進行優化來減小其空間佔用。要壓縮素數表的空間佔用,可以使用位操作。下面是用篩素數法計算100以內的素數示例程式碼(注2):
// 列印100以內素數:
// (1)對每個素數,它的倍數必定不是素數;
// (2)有很多重複訪問如flag[10]會在訪問flag[2]和flag[5]時各訪問一次;
int max = 100;
boolean[] flags = new boolean[max];
int [] primes = new int[max / 3 + 1];
int pi = 0;
for (int m = 2; m < max ; m ++) {
if (!flags[m]) {
primes[pi++] = m;
for(int n = m; n < max; n += m) {
flags[n] = true;
}
}
}
System.out.println(Arrays.toString(primes));
執行結果如下:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0]
在上面程式是用bool陣列來作標記的,bool型資料佔1個位元組(8位),因此用位操作來壓縮下空間佔用將會使空間的佔用減少八分之七。
下面考慮下如何在陣列中對指定位置置1,先考慮如何對一個整數在指定位置上置1。對於一個整數可以通過將1向左移位後與其相或來達到在指定位上置1的效果,程式碼如下所示:
// 在一個數指定位上置1
int e = 0;
e |= 1 << 10;
System.out.println(e);
同樣,可以1向左移位後與原數相與來判斷指定位上是0還是1(也可以將原數右移若干位再與1相與)。
//判斷指定位上是0還是1
if ((e & (1 << 10)) != 0)
System.out.println("指定位上為1");
else
System.out.println("指定位上為0");
擴充套件到陣列上,我們可以採用這種方法,因為陣列在記憶體上也是連續分配的一段空間,完全可以“認為”是一個很長的整數。先寫一份測試程式碼,看看如何在陣列中使用位操作:
int[] bits = new int[40];
for (int m = 0; m < 40; m += 3) {
bits[m / 32] |= (1 << (m % 32));
}
// 輸出整個bits
for (int m = 0; m < 40; m++) {
if (((bits[m / 32] >> (m % 32)) & 1) != 0)
System.out.print('1');
else
System.out.print('0');
}
執行結果如下:
1001001001001001001001001001001001001001
可以看出該陣列每3個就置成了1,證明我們上面對陣列進行位操作的方法是正確的。因此可以將上面篩素數方法改成使用位操作壓縮後的篩素數方法:
int[] flags2 = new int[max / 32 + 1];
pi = 0;
for (int m = 2; m < max ; m ++) {
if ((((flags2[m / 32] >> (m % 32)) & 1) == 0)) {
primes[pi++] = m;
for(int n = m; n < max; n += m) {
flags2[n / 32] |= (1 << (n % 32));
}
}
}
System.out.println();
System.out.println(Arrays.toString(primes));
執行結果如下:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0]
位操作工具類
package com.king.bit;
/**
* Java 位運算的常用方法封裝<br>
*/
public class BitUtils {
/**
* 獲取運算數指定位置的值<br>
* 例如: 0000 1011 獲取其第 0 位的值為 1, 第 2 位 的值為 0<br>
*
* @param source
* 需要運算的數
* @param pos
* 指定位置 (0<=pos<=7)
* @return 指定位置的值(0 or 1)
*/
public static byte getBitValue(byte source, int pos) {
return (byte) ((source >> pos) & 1);
}
/**
* 將運算數指定位置的值置為指定值<br>
* 例: 0000 1011 需要更新為 0000 1111, 即第 2 位的值需要置為 1<br>
*
* @param source
* 需要運算的數
* @param pos
* 指定位置 (0<=pos<=7)
* @param value
* 只能取值為 0, 或 1, 所有大於0的值作為1處理, 所有小於0的值作為0處理
*
* @return 運算後的結果數
*/
public static byte setBitValue(byte source, int pos, byte value) {
byte mask = (byte) (1 << pos);
if (value > 0) {
source |= mask;
} else {
source &= (~mask);
}
return source;
}
/**
* 將運算數指定位置取反值<br>
* 例: 0000 1011 指定第 3 位取反, 結果為 0000 0011; 指定第2位取反, 結果為 0000 1111<br>
*
* @param source
*
* @param pos
* 指定位置 (0<=pos<=7)
*
* @return 運算後的結果數
*/
public static byte reverseBitValue(byte source, int pos) {
byte mask = (byte) (1 << pos);
return (byte) (source ^ mask);
}
/**
* 檢查運算數的指定位置是否為1<br>
*
* @param source
* 需要運算的數
* @param pos
* 指定位置 (0<=pos<=7)
* @return true 表示指定位置值為1, false 表示指定位置值為 0
*/
public static boolean checkBitValue(byte source, int pos) {
source = (byte) (source >>> pos);
return (source & 1) == 1;
}
/**
* 入口函式做測試<br>
*
* @param args
*/
public static void main(String[] args) {
// 取十進位制 11 (二級制 0000 1011) 為例子
byte source = 11;
// 取第2位值並輸出, 結果應為 0000 1011
for (byte i = 7; i >= 0; i--) {
System.out.printf("%d ", getBitValue(source, i));
}
// 將第6位置為1並輸出 , 結果為 75 (0100 1011)
System.out.println("\n" + setBitValue(source, 6, (byte) 1));
// 將第6位取反並輸出, 結果應為75(0100 1011)
System.out.println(reverseBitValue(source, 6));
// 檢查第6位是否為1,結果應為false
System.out.println(checkBitValue(source, 6));
// 輸出為1的位, 結果應為 0 1 3
for (byte i = 0; i < 8; i++) {
if (checkBitValue(source, i)) {
System.out.printf("%d ", i);
}
}
}
}
BitSet類
BitSet類:大小可動態改變, 取值為true或false的位集合。用於表示一組布林標誌。 此類實現了一個按需增長的位向量。位 set 的每個元件都有一個 boolean 值。用非負的整數將 BitSet 的位編入索引。可以對每個編入索引的位進行測試、設定或者清除。通過邏輯與、邏輯或和邏輯異或操作,可以使用一個 BitSet 修改另一個 BitSet 的內容。預設情況下,set 中所有位的初始值都是 false。
每個位 set 都有一個當前大小,也就是該位 set 當前所用空間的位數。注意,這個大小與位 set 的實現有關,所以它可能隨實現的不同而更改。位 set 的長度與位 set 的邏輯長度有關,並且是與實現無關而定義的。
除非另行說明,否則將 null 引數傳遞給 BitSet 中的任何方法都將導致 NullPointerException。 在沒有外部同步的情況下,多個執行緒操作一個 BitSet 是不安全的。
建構函式: BitSet() or BitSet(int nbits),預設初始大小為64。
public void set(int pos): 位置pos的字位設定為true。
public void set(int bitIndex, boolean value): 將指定索引處的位設定為指定的值。
public void clear(int pos): 位置pos的字位設定為false。
public void clear(): 將此 BitSet 中的所有位設定為 false。
public int cardinality(): 返回此 BitSet 中設定為 true 的位數。
public boolean get(int pos): 返回位置是pos的字位值。
public void and(BitSet other): other同該字位集進行與操作,結果作為該字位集的新值。
public void or(BitSet other): other同該字位集進行或操作,結果作為該字位集的新值。
public void xor(BitSet other): other同該字位集進行異或操作,結果作為該字位集的新值。
public void andNot(BitSet set): 清除此 BitSet 中所有的位,set - 用來遮蔽此 BitSet 的 BitSet
public int size(): 返回此 BitSet 表示位值時實際使用空間的位數。
public int length(): 返回此 BitSet 的“邏輯大小”:BitSet 中最高設定位的索引加 1。
public int hashCode(): 返回該集合Hash 碼, 這個碼同集合中的字位值有關。
public boolean equals(Object other): 如果other中的字位同集合中的字位相同,返回true。
public Object clone(): 克隆此 BitSet,生成一個與之相等的新 BitSet。
public String toString(): 返回此位 set 的字串表示形式。
例1:標明一個字串中用了哪些字元
package com.king.bit;
import java.util.BitSet;
public class WhichChars {
private BitSet used = new BitSet();
public WhichChars(String str) {
for (int i = 0; i < str.length(); i++)
used.set(str.charAt(i)); // set bit for char
}
public String toString() {
String desc = "[";
int size = used.size();
for (int i = 0; i < size; i++) {
if (used.get(i))
desc += (char) i;
}
return desc + "]";
}
public static void main(String args[]) {
WhichChars w = new WhichChars("How do you do");
System.out.println(w);
}
}
例2:
package com.king.bit;
import java.util.BitSet;
public class MainTestThree {
/**
* @param args
*/
public static void main(String[] args) {
BitSet bm = new BitSet();
System.out.println(bm.isEmpty() + "--" + bm.size());
bm.set(0);
System.out.println(bm.isEmpty() + "--" + bm.size());
bm.set(1);
System.out.println(bm.isEmpty() + "--" + bm.size());
System.out.println(bm.get(65));
System.out.println(bm.isEmpty() + "--" + bm.size());
bm.set(65);
System.out.println(bm.isEmpty() + "--" + bm.size());
}
}
例3:
package com.king.bit;
import java.util.BitSet;
public class MainTestFour {
/**
* @param args
*/
public static void main(String[] args) {
BitSet bm1 = new BitSet(7);
System.out.println(bm1.isEmpty() + "--" + bm1.size());
BitSet bm2 = new BitSet(63);
System.out.println(bm2.isEmpty() + "--" + bm2.size());
BitSet bm3 = new BitSet(65);
System.out.println(bm3.isEmpty() + "--" + bm3.size());
BitSet bm4 = new BitSet(111);
System.out.println(bm4.isEmpty() + "--" + bm4.size());
}
}
位操作技巧
// 1. 獲得int型最大值
System.out.println((1 << 31) - 1);// 2147483647, 由於優先順序關係,括號不可省略
System.out.println(~(1 << 31));// 2147483647
// 2. 獲得int型最小值
System.out.println(1 << 31);
System.out.println(1 << -1);
// 3. 獲得long型別的最大值
System.out.println(((long)1 << 127) - 1);
// 4. 乘以2運算
System.out.println(10<<1);
// 5. 除以2運算(負奇數的運算不可用)
System.out.println(10>>1);
// 6. 乘以2的m次方
System.out.println(10<<2);
// 7. 除以2的m次方
System.out.println(16>>2);
// 8. 判斷一個數的奇偶性
System.out.println((10 & 1) == 1);
System.out.println((9 & 1) == 1);
// 9. 不用臨時變數交換兩個數(面試常考)
a ^= b;
b ^= a;
a ^= b;
// 10. 取絕對值(某些機器上,效率比n>0 ? n:-n 高)
int n = -1;
System.out.println((n ^ (n >> 31)) - (n >> 31));
/* n>>31 取得n的符號,若n為正數,n>>31等於0,若n為負數,n>>31等於-1
若n為正數 n^0-0數不變,若n為負數n^-1 需要計算n和-1的補碼,異或後再取補碼,
結果n變號並且絕對值減1,再減去-1就是絕對值 */
// 11. 取兩個數的最大值(某些機器上,效率比a>b ? a:b高)
System.out.println(b&((a-b)>>31) | a&(~(a-b)>>31));
// 12. 取兩個數的最小值(某些機器上,效率比a>b ? b:a高)
System.out.println(a&((a-b)>>31) | b&(~(a-b)>>31));
// 13. 判斷符號是否相同(true 表示 x和y有相同的符號, false表示x,y有相反的符號。)
System.out.println((a ^ b) > 0);
// 14. 計算2的n次方 n > 0
System.out.println(2<<(n-1));
// 15. 判斷一個數n是不是2的冪
System.out.println((n & (n - 1)) == 0);
/*如果是2的冪,n一定是100... n-1就是1111....
所以做與運算結果為0*/
// 16. 求兩個整數的平均值
System.out.println((a+b) >> 1);
// 17. 從低位到高位,取n的第m位
int m = 2;
System.out.println((n >> (m-1)) & 1);
// 18. 從低位到高位.將n的第m位置為1
System.out.println(n | (1<<(m-1)));
/*將1左移m-1位找到第m位,得到000...1...000
n在和這個數做或運算*/
// 19. 從低位到高位,將n的第m位置為0
System.out.println(n & ~(0<<(m-1)));
/* 將1左移m-1位找到第m位,取反後變成111...0...1111
n再和這個數做與運算*/
相關推薦
Java位操作全面總結
在計算機中所有資料都是以二進位制的形式儲存的。位運算其實就是直接對在記憶體中的二進位制資料進行操作,因此處理資料的速度非常快。在實際程式設計中,如果能巧妙運用位操作,完全可以達到四兩撥千斤的效果,正因為位操作的這些優點,所以位操作在各大IT公司的筆試面試中一直是個熱點問題。 位操作基礎 基本的位操作符有與、
C++:位操作基礎篇之位操作全面總結
位操作篇共分為基礎篇和提高篇,基礎篇主要對位操作進行全面總結,幫助大家梳理知識。提高篇則針對各大IT公司如微軟、騰訊、百度、360等公司的筆試面試題作詳細的解答,使大家能熟練應對在筆試面試中位操作題目。 下面就先來對位操作作個全面總結,歡迎大家補充。 在計
java位操作總結
int a = -2; //1111 1111 1111 1111 1111 1111 1111 1110 a = a<<3; System.out.println(a); //a=-16 1111 1111 1111 1111 1111 1111 1111 0000 in
JAVA類的全面總結
java是面向物件: 下面解釋什麼面向物件 JAVA操作都是類中操作如上圖Cat類,例項化可以在main函式中 那麼java本身就是類操作,為什麼還要學習繼承多型介面包? 官方回答:可讀性強,安全性高,程式碼簡化,維護性高,方便查詢錯誤等 下面我要從現實生活的角度
[Java] 位操作
Java中的位操作及強制型別轉換 進製表示 Java中提供了表示8,16進位制的方法 int a=0b11; int b=011; int
可能是最通俗易懂的 Java 位操作運算講解
Java 位操作這是一項很基礎很基礎的知識內容,在所有 Android 和 Java 開發者的學習之路上,大家都接觸過,但是實際運用的場景卻很少見,很多人估計都忘記有這個知識點了。事實上,在 C/C++ 開發領域因為與硬體的聯絡更緊密,所以位操作運算應用的更普遍
轉: 【Java並發編程】之十七:深入Java內存模型—內存操作規則總結
tle 沒有 article 類型 javase 感知 執行引擎 要求 lock 轉載請註明出處:http://blog.csdn.net/ns_code/article/details/17377197 主內存與工作內存 Java內存模型的主要目標是定義程序中
java中的位操作
ble 其他 自動 byte 有符號 cnblogs 運算 編譯 位移操作 之前做項目的時候使用位操作不是很多,今天在刷leetcode上題目的時候用到了位操作,是leetcode中的第29題Divide Two Integers。 一、java的位操作: 位運算表達式由操
Java運算符使用總結(重點:自增自減、位運算和邏輯運算)
運算 計算器 可讀性 過多 移位運算 style avi 學會 new Java運算符共包括這幾種:算術運算符、比較運算符、位運算符、邏輯運算符、賦值運算符和其他運算符。(該圖來自網絡) 簡單的運算符,就不過多介紹使用了,可自行測試。關於賦值運算,可以結合算術運
JAVA 集合操作總結
dna arr -o2 inter map 實現 void array 遍歷 1.Collection 1.基本操作 對集合的基礎操作 1.boolean add(Object o) //添加對象到集合 2.boolean remove(Object o) //刪除指定的
java Integer中的方法解析(位操作)
方法 描述 static int bitCount(int i) 返回i的二進位制中1的個數. static int compare(int x, int y)
Java IO流常用操作方法總結
一、簡介 在實際工作中,基本上每個專案難免都會有檔案相關的操作,比如檔案上傳、檔案下載等,這些操作都是使用IO流進行操作的,本文將通過簡單的示例對常用的一些IO流進行總結。 二、使用詳解 【a】FileInputStream與FileOutputStream 首先通過檢視jdk文件,
jsp頁面中JSTL/EL標籤引用java後臺靜態static欄位的方法總結
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
mysql修改表結構 MySQL修改表結構操作命令總結 MySql資料庫在表中新增新欄位,設定主鍵,設定外來鍵,欄位移動位置,以及修改資料庫後如何進行部署和維護的總結 mysql 如何修改、新增、刪除表主鍵
MySQL修改表結構操作命令總結 以下內容轉自:http://www.jb51.net/article/58079.htm 表的結構如下: 複製程式碼程式碼如下:
Selenium Java Web 自動化實踐總結(六)元素操作例項
先上介面和html程式碼,頁面包含按鈕,單選框,複選框,下拉框,輸入框這幾種頁面常用元素 html程式碼如下 <html> <head>演示用html</head> <body> <br><br><br>
一位老鳥的JAVA面試和基礎總結
前言 畢業至今已有4年的時間,近兩年期間陸續面試了不少的求職的前(JAVA)、後(WEB)端開發人員,包括實習生、應屆畢業生、一兩年工作經驗的、也有三四年工作經驗的,也算見過了比較多的開發人員,想在這裡做個總結,本次主要講一講面試和後端(java)相關的東西; 關於面試準備
JAVA中常用的二進位制位操作
一,計算某個正數的二進位制表示法中 1 的個數 1 //求解正數的二進位制表示法中的 1 的位數 2 private static int countBit(int num){ 3 int count = 0; 4 for
Java檔案操作及編碼總結
編碼/解碼 編碼:getBytes(); 按照預設編碼表編碼 字串-------->位元組 解碼:new String(); 按照預設編碼表解碼 位元組------->>字串 GBK 碼錶:漢字的儲存,第一個一定是負的。如果轉換器讀到的
2018全面總結阿里巴巴Java開發手冊
其實早在多年前,Google就已經把公司內部採用的所有語言的編碼規範(其稱為 Style Guide )都開源在Github上。這份清單中包括了 C++ 、 Objective-C 、 Java 、 Python 、 R 、 Shell 、 HTML/CSS 、 JavaScript 、 A
java操作mongodb總結
新需求需要用到Mongodb去存APP過來的資料,所以就簡單學了一下。 專案是用Spring搭建的,本來直接用spring-data-mongodb這個模組就很簡單地實現操作mongodb的功能,但是,經理硬是不讓,必須使用mongodb原生的驅動去實現。給我