您好,登錄后才能下訂單哦!
位運算是編程語言的基礎,在看源碼的時候會看到很多位運算代碼,但是在項目代碼中很少會看到位運算。因為應用代碼中,有很多判斷和計算都可以直接用數值的判斷和計算完成,沒有必要去用位運算,以至于這些基礎的東西慢慢用的越來越少,慢慢也就忘了。導致的一個結果就是看源代碼很費力,因為大量的位運算邏輯,看不懂。
作為程序員感覺數據位運算是非常必要,有點如下:
運算規則:
操作數1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|
操作數2 | 0 | 1 | 0 | 1 |
與運算 | 0 | 0 | 0 | 1 |
規則總結:只有兩個操作數都是1的時候運算結果才為1,其他都是0。換句話說就是只要包含0就是0。
運算如下:
運算規則:
操作數1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|
操作數2 | 0 | 1 | 0 | 1 |
或運算 | 0 | 1 | 1 | 1 |
規則總結:只有兩個操作數都是0的時候運算結果才為0,其他都是1。換句話說就是只要包含1就是1。
運算如下:
運算規則:
操作數 | 1 | 0 |
---|---|---|
或運算 | 0 | 1 |
規則總結:取反操作,1變0,0變1。
運算規則:
操作數1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|
操作數2 | 0 | 1 | 0 | 1 |
或運算 | 0 | 1 | 1 | 0 |
規則總結:操作數相同,運算結果就是0,反之操作數不同就為1。
在二進制里面總共有32位,0-31,第31位是表示當前數值的正負,當時0的時候表示這個數值是正數,當是1表示這個數值是負數。
以2<<2為例。
2:00000000 00000000 00000000 00000010
向左移動兩位,右側會空出來兩個位置,兩個位置用0補位得到的結果如下:
8:00000000 00000000 00000000 00001000
轉換成十進制對應的數值為8。因此可以得到2<<2的結果是8。
以-2<<2為例。
-2:11111111 11111111 11111111 11111110
向左移動兩位,右側空出的兩個位置用0補位,到這里還沒有結束,要是想計算出它的值,還要做補位,那就是將當前移位得到的結果(11111111 11111111 11111111 11111000)減一后再取反碼,得的結果就是補碼的結果。
減一操作:
11111111 11111111 11111111 11110111
取反碼的結果:
00000000 00000000 00000000 00001000
這個對應的值是8,因為是負值,那么得到的結果就是-8,因此-2<<2的結果是-8。
這里可以看出來,在左移的時候,不論這個目標值(2或-2)是正數還是負數,結果都符合一個規律,即表達式:$m*2^n$。m表示目標值,n表示的是移位的位數。
-2<<2 = -8,2<<2 = 8,同理可以得到2<<4 = 2x2^4 = 32,-2<<4 = -2x2^4 = -32。
以10>>2為例。
10:00000000 00000000 00000000 00001010
右移兩位,右側的10就會被舍棄,左側會空出兩個空位,空位用符號位補齊,前面說過正數的符號位是0,也就是用0補齊,得到的結果如下:
2:00000000 00000000 00000000 00000010
得到的十進制結果是2,結論就是10>>2=2。
以-10>>2為例。
-10:11111111 11111111 11111111 11110110
同樣是右移,溢出的部分舍棄,空位的部分用符號位補齊,得到的結果如下:
11111111 11111111 11111111 11111101
減一操作后結果:
11111111 11111111 11111111 11111100
取反碼后的結果:
00000000 00000000 00000000 00000011
對應的十進制結果是3,因為是負數,那么結果就是-3。結論是-10>>2。
整體的總結來看,正數的左移和右移沒有什么問題,但是負數存在一個補碼的問題,比較麻煩。補碼記住一個口訣就可以,移位、補碼、減一、取反碼,得到正數結果加個負號。這樣就可以得到正確結果。
Java中沒有無符號左移的說法,這里只說右移。同樣也是分正數和負數來講。
以10>>>2為例。
10:00000000 00000000 00000000 00001010
右移后,左側空出的位置用0補齊,但是這里需要注意的是這個0并不是指符號位,只是一個普通的補位。得到的結果如下:
2:00000000 00000000 00000000 00000010
得到的十進制結果是2,結論就是10>>2=2。這個和有符號位移是得到相同的結果。
以-10>>>2為例。
-10:11111111 11111111 11111111 11110110
右移后,左側空位用0補,注意不是用1補,后面說原因。
00111111 11111111 11111111 11111101
這個結果就很大了,結果是1073741821,負數變成了這么大的負數,不要懷疑自己的眼神,這個結果是正確的。
所謂的無符號右移,就是將原有的二進制值直接右移得到結果,不論是負數還是正數,沒有補碼的操作,補位都統一使用0,而不是對應的符號位1或0。
到這里N種基本的位運算已經說明結束,接下來看幾個例子,和在代碼中常用的一些技巧。
// true表示為奇數,false表示為偶數
public boolean checkNum(int num){
return (num&1) == 1;
}
1的二進制是00000000 00000000 00000000 00000001,&運算的規則是只有都是1,結果才是1,很明顯,不管是什么數,和1進行&運算,前31位都是0,只有最后一位可能得出不同的結果。偶數最后一位肯定是0,奇數肯定是1,那么結果就顯而易見了。
// 傳入的a=3,b=5
public void swap(int a,int b){
a ^= b;// a = a^b;
b ^= a;// b = b^a;
a ^= b;// a = a^b;
System.out.println("a="+a);
System.out.println("b="+b);
}
/*
輸出結果:
a=5
b=3
*/
這個靈活的用到了異或來實現。下面做個簡單的說明。
a = 3:00000011
b = 5:00000101
a異或b得到的結果是:00000110,這個結果賦值給a;
b異或a得到的結果是:00000011,這個結果賦值給b;
a異或b得到的結果是:00000101,這個結果賦值給a;
所以最終得到的結果是:
a = 5:00000101
b = 3:00000011
public int getAbs(int n){
return (n ^ (n >> 31)) - (n >> 31);
}
具體不說,可以自己嘗試著寫一下。(因為涉及到正數、負數的舉例,然后負數又有補碼操作,要寫的內容太多,本博主就傲嬌一下,不寫啦!)
其實這里的例子很多,就不都去說了,有興趣的可以度娘一下相關的博客,有很多文章。
涉及到公司的東西,這里不會寫的很具體,給思路。
現在有一個訂單,訂單可以進行很多操作,如:創建、修改、退單、接單、終止、派單、提交完成等操作。同時訂單也有很多狀態,如:待接單、進行中、已終止、已完成等狀態。現在的場景就是不同的狀態對應的操作是不一樣的,前端需要根據不同的狀態顯示不同的操作控件。
思路一:
最簡單也是最麻煩的,用N個flag字段表示可進行的操作,返回數據的時候對每個flag字段賦值true、false,表示是否能夠進行此操作。但是這樣存在一個巨大的問題,當后期有一個操作去掉了或者添加了幾種新的操作,這個時候就涉及到字段的刪除和新增,改動較大,可維護性差,不夠靈活。
思路二:(推薦)
使用此二進制的方式表示。實現如下:
第一位:0,1表示是否可以修改
第二位:0,1表示是否可以接單
第三位:0,1表示是否可以終止
返回給前端只要一個字段tags,如對應的值是:010,那就表示不可以修改、可以接單、不可以終止。
如果現在終止操作不要了,那么這個二進制位始終給0就可以。如果新增了一個提交操作,那就讓第四位表示是否可以提交。這樣不需要添加字段,只要在原字段加一位就OK。
另外還有其他很多應用場景。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。