你應該知道的一些“位運算”……


你應該知道的一些“位運算”……

相信很多同學在Java基礎學習過程中,對於常見常用的各種運算符號都有一定的心得了。特別是我們很容易學習和理解的算術運算符(+,-,*,/,%,++,–),賦值運算符(=,+=,-=,*=,/=,%=),比較運算符(>,=,<=,==,!=),邏輯運算符(&&,||,!),三目運算符( ? :)。

但是當看到位運算符的時候,很多沒有基礎的同學可能有一點懵逼吧。

其實,位運算在某些時候還是非常有用的。特別是在底層編碼的時候,它可以帶來很多性能以及語法上的優化。

當然,我們應用層的開發人員可能用得比較少。不過,多瞭解一些還是很有好處的,所以接下來我們就聊聊那些你應該知道的“位運算”。

什麼是位運算


我們知道程序中的所有數在計算機內存中都是以二進制的形式儲存的。位運算說白了,就是直接對整數在內存中的二進制位進行操作。

因此,位運算最直接的好處就是節省內存空間,提高運算效率。在學習位運算符的時候,我們就可以根據這個概念,先確定兩個前提:

  1. 位運算符都是以二進制的方式進行運算;
  2. 位運算符都是操作的整數;

位與(&)

規則:位與(&)是一個雙目運算符,也就是說&符號的左右各有一個操作數。它是把兩個操作數在二進制的形式上按位進行比較,如果都是1,則結果的這一位就為1;否則就為0。

示例:

1 public static void main(String[] args){
2 int a = 129;
3 int b = 128;
4 System.out.println(a & b);
5 }
6 //運行結果為:128

我們來分析解讀一下這個程序:變量a的值是129,轉換成二進制就是10000001,而變量b的值是128,轉換成二進制就是10000000。

根據位與(&)運算符的運算規律,只有兩個位都是1,結果才是1,可以知道結果就是10000000,即128。

位與(&)符號,如果左右兩邊操作數是boolean類型的true或false的時候,我們完全可以把true看作是一個1,false看作是一個0。這樣,根據規則位與(&)就會起到和邏輯與(&&)一樣的作用了。

它的左右兩端必須同時為true,整個表達式就為true,只要有一個false,這個表達式就為false。

1 public static void main(String[] args){
2 System.out.println(true & true);//打印:true
3 System.out.println(true & false);//打印:false
4 System.out.println(false & true);//打印:false
5 System.out.println(false & false);//打印:false
6 }


應用:

在實際開發中,位與(&)對我們最大的用處是作為邏輯運算符—邏輯與(&&)的補充使用。邏輯與(&&)有一個特點就是短路,而位與(&)就沒有短路的效果。

所以,當我們需要在與操作的時候要求:不管第1個表達式的結果如何,都必須要運算第2個表達式的時候,我們就可以選用位與(&)符號替代邏輯與(&&)。

位或(|)

規則:位或(|)也是一個雙目運算符。它是把兩個操作數在二進制的形式上按位進行比較,只要有一個是1,則結果的這一位就為1;否則就為0。

示例:

1 public static void main(String[] args){
2 int a = 129;
3 int b = 128;
4 System.out.println(a | b);
5 }
6 //運行結果為:129


我們來分析解讀一下這個程序:變量a的值是129,轉換成二進制就是10000001,而變量b的值是128,轉換成二進制就是10000000。根據位或(|)運算符的運算規律,只要某個位是1,結果位就是1,可以知道結果就是10000001,即129。

位或(|)符號,如果左右兩邊操作數是boolean類型的true或false的時候,我們同樣可以把true看作是一個1,false看作是一個0。

這樣,根據規則位或(|)就會起到和邏輯或(||)一樣的作用了。它的左右兩端只要一個為true,整個表達式就為true;必須兩個都是false,整個表達式才為false。

1 public static void main(String[] args){
2 System.out.println(true | true);//打印:true
3 System.out.println(true | false);//打印:true
4 System.out.println(false | true);//打印:true
5 System.out.println(false | false);//打印:false
6 }


應用:

在實際開發中,位或(|)同樣是用來對邏輯或(||)做替代的。使用位或(|)操作boolean表達式的時候,效果與邏輯或(||)一樣,且沒有短路效果。

位非(~)

規則:位非(~)是一個單目運算符,語法上它的右邊有一個整數作為操作數。運算時,會把這個操作數的二進制按位進行1變0、0變1的操作。

示例:

1 public static void main(String[] args){
2 int a = 4;
3 System.out.println(~a);//打印:-5
4 }

我們來分析一下:


你應該知道的一些“位運算”……

所以最終結果為-5。

應用:

位非運算符當然也可以操作boolean表達式,只不過使用它和使用邏輯非(!)沒有什麼區別,所以在應用層開發中這個運算符使用率並不高。

異或(^)

規則:異或(^)是一個雙目運算符。運算時,它是把兩個操作數在二進制的形式上按位進行比較,如果都是1或都是0(相同),則結果的這一位就為0;否則兩個對應位不同(一個0和一個1),結果位就為1。

示例:

1
2
3
4
5
6
public static void main(String[] args){
int a = 15;
int b = 2;
System.out.println(a ^ b);
}
//運行結果為:13


分析解讀一下這個程序:變量a的值是15,轉換成二進制為1111,而變量b的值是2,轉換成二進制為0010,根據異或的運算規律(相同為0,不同為1),可以得出其結果為1101 即13。

應用:

異或(^)運算有兩個非常有趣的結論:

  1. 任何一個數異或(^)它本身,結果是0;
  2. 任何一個數異或(^)0,結果是它本身。

a ^ a 的結果是0;

a ^ a ^ a 的結果是 a;

a ^ a ^ a ^ a 的結果是0;

a ^ a ^ a ^ a ^ a 的結果又是a。

所以,異或(^)運算符又被稱之為“翻面”,每異或(^)一次自己就翻一個面。

左移(<

規則:將運算符左邊的整數(二進制形式)向左移動運算符右邊指定的位數(在低位補0)。

示例一:

1
2
3
4
public static void main(String[] args){
System.out.println(5 << 2);
}
//運行結果為:20


分析解讀一下這個程序:

你應該知道的一些“位運算”……

所以結果是:20。

示例二:

1
2
3
4
public static void main(String[] args){
System.out.println(-4 << 2);
}
//運行結果為:-16


分析解讀一下這個程序:

你應該知道的一些“位運算”……

所以結果是:-16。

應用:

左移(<

右移(>>)

規則:將運算符左邊的整數(二進制形式)向右移動運算符右邊指定的位數。使用符號擴展機制,也就是說,如果值為正,則在高位補0,如果值為負,則在高位補1。

示例一:

1
2
3
4
public static void main(String[] args){
System.out.println(13 >> 2);
}
//運行結果為:3


分析解讀一下這個程序:

你應該知道的一些“位運算”……

所以結果是:3。

示例二:

1
2
3
4
public static void main(String[] args){
System.out.println(-13 >> 2);
}
//運行結果為:-4


分析解讀一下這個程序:


你應該知道的一些“位運算”……

所以結果是:-4。

應用:

右移(>>)運算就是一種除法運算,任何一個整數右移多少位,就是把這個整數除以2的多少次方。這種運算在效率和性能上都比算術運算中的除法(/)要高很多。

注意:

同學們會發現右移一個正整數確實會得到除法的效果,但是右移一個負整數得到的結果會比除法的效果要小一個數。

13 >> 2 得到 3

13 / 4 得到 3

-13 >> 2 得到 -4

-13 / 4 得到 -3

這是因為計算機在除不盡的時候統統採用的是向下取整,而我們人則習慣於直接去掉小數部分。不同的計算機語言在設計除法運算的時候有些會直接按計算機的方式來,有些會按人的習慣來。

這裡很明顯,Java語言在設計除法運算符的時候採用了人的自然習慣。

無符號右移(>>>)

規則:將運算符左邊的整數(二進制)向右移動運算符右邊指定的位數。採用0擴展機制,也就是說,無論值的正負,都在高位補0。

示例:

1
2
3
4
public static void main(String[] args){
System.out.println(-4 >>> 2);
}
//運行結果為:1073741823


分析解讀一下這個程序:

你應該知道的一些“位運算”……

所以結果是:1073741823。

應用:

無符號右移(>>>)由於高位一定會補0,所以最後的結果一定會是一個正數。但它的計算沒有任何數學意義,只有邏輯意義。它主要出現在散列、加密、壓縮、影音媒體編碼等技術上。

位運算的常見使用技巧


由於位運算符是直接針對二進制數據進行操作,而計算機內部就是直接以二進制的形式表示數據的,所以使用位運算符在效率上往往要比其他運算符高上很多。

只是說使用位運算符做比較複雜的運算時,對於我們“普通人”來說比較難於考慮和理解,所以讓很多人望而卻步。

下面,我們就介紹幾個比較簡單的、作為“普通人”也能用得上的常見技巧。

  1. 使用在邏輯運算中,使用位與(&)和位或(|)替代邏輯與(&&)和邏輯或(||),從而達到不短路的效果。—日常開發中常用
  2. 使用異或(^)完成兩個變量的交換。—面試常見
1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args){
int a = 10;
int b = 7;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
運行結果:
a = 7
b = 10
  1. 計算絕對值;
1
2

3
4
5
6
7
8
public static void main(String[] args){
int x = -5;
\tint y = x >> 31;
int result = (x + y) ^ y;//或(x ^ y)- y
System.out.println(x + "的絕對值是:" + result);
}
運行結果:
-5的絕對值是:5
  1. 判斷int變量是否是奇數或偶數;
  2. a & 1 == 0;——偶數
  3. a & 1 == 1;——奇數
  4. 求兩個int的平均數;
1
2
3
4
5
6
7
8
public static void main(String[] args){
int x = -5;
int y = 21;
int result = (x & y) + ((x ^ y) >> 1);
System.out.println("x與y的平均數是:" + result);
}
運行結果:
x與y的平均數是:8
  1. m乘以2的n次方 等價於 m << n;
  2. m除以2的n次方 等價於 m >> n;
  3. x = (x == a) ? b : a 等價於 x = a ^ b ^ x;


以上就是今天的分享啦~

技術乾貨可以私信發送【微信】加朗妹兒微信喲~


分享到:


相關文章: