位運(yùn)算是編程語言的基礎(chǔ),在看源碼的時(shí)候會(huì)看到很多位運(yùn)算代碼,但是在項(xiàng)目代碼中很少會(huì)看到位運(yùn)算。因?yàn)閼?yīng)用代碼中,有很多判斷和計(jì)算都可以直接用數(shù)值的判斷和計(jì)算完成,沒有必要去用位運(yùn)算,以至于這些基礎(chǔ)的東西慢慢用的越來越少,慢慢也就忘了。導(dǎo)致的一個(gè)結(jié)果就是看源代碼很費(fèi)力,因?yàn)榇罅康奈贿\(yùn)算邏輯,看不懂。
作為程序員感覺數(shù)據(jù)位運(yùn)算是非常必要,有點(diǎn)如下:
海拉爾ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!
運(yùn)算規(guī)則:
操作數(shù)1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|
操作數(shù)2 | 0 | 1 | 0 | 1 |
與運(yùn)算 | 0 | 0 | 0 | 1 |
規(guī)則總結(jié):只有兩個(gè)操作數(shù)都是1的時(shí)候運(yùn)算結(jié)果才為1,其他都是0。換句話說就是只要包含0就是0。
運(yùn)算如下:
運(yùn)算規(guī)則:
操作數(shù)1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|
操作數(shù)2 | 0 | 1 | 0 | 1 |
或運(yùn)算 | 0 | 1 | 1 | 1 |
規(guī)則總結(jié):只有兩個(gè)操作數(shù)都是0的時(shí)候運(yùn)算結(jié)果才為0,其他都是1。換句話說就是只要包含1就是1。
運(yùn)算如下:
運(yùn)算規(guī)則:
操作數(shù) | 1 | 0 |
---|---|---|
或運(yùn)算 | 0 | 1 |
規(guī)則總結(jié):取反操作,1變0,0變1。
運(yùn)算規(guī)則:
操作數(shù)1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|
操作數(shù)2 | 0 | 1 | 0 | 1 |
或運(yùn)算 | 0 | 1 | 1 | 0 |
規(guī)則總結(jié):操作數(shù)相同,運(yùn)算結(jié)果就是0,反之操作數(shù)不同就為1。
在二進(jìn)制里面總共有32位,0-31,第31位是表示當(dāng)前數(shù)值的正負(fù),當(dāng)時(shí)0的時(shí)候表示這個(gè)數(shù)值是正數(shù),當(dāng)是1表示這個(gè)數(shù)值是負(fù)數(shù)。
以2<<2為例。
2:00000000 00000000 00000000 00000010
向左移動(dòng)兩位,右側(cè)會(huì)空出來兩個(gè)位置,兩個(gè)位置用0補(bǔ)位得到的結(jié)果如下:
8:00000000 00000000 00000000 00001000
轉(zhuǎn)換成十進(jìn)制對(duì)應(yīng)的數(shù)值為8。因此可以得到2<<2的結(jié)果是8。
以-2<<2為例。
-2:11111111 11111111 11111111 11111110
向左移動(dòng)兩位,右側(cè)空出的兩個(gè)位置用0補(bǔ)位,到這里還沒有結(jié)束,要是想計(jì)算出它的值,還要做補(bǔ)位,那就是將當(dāng)前移位得到的結(jié)果(11111111 11111111 11111111 11111000)減一后再取反碼,得的結(jié)果就是補(bǔ)碼的結(jié)果。
減一操作:
11111111 11111111 11111111 11110111
取反碼的結(jié)果:
00000000 00000000 00000000 00001000
這個(gè)對(duì)應(yīng)的值是8,因?yàn)槭秦?fù)值,那么得到的結(jié)果就是-8,因此-2<<2的結(jié)果是-8。
這里可以看出來,在左移的時(shí)候,不論這個(gè)目標(biāo)值(2或-2)是正數(shù)還是負(fù)數(shù),結(jié)果都符合一個(gè)規(guī)律,即表達(dá)式:$m*2^n$。m表示目標(biāo)值,n表示的是移位的位數(shù)。
-2<<2 = -8,2<<2 = 8,同理可以得到2<<4 = 2x2^4 = 32,-2<<4 = -2x2^4 = -32。
以10>>2為例。
10:00000000 00000000 00000000 00001010
右移兩位,右側(cè)的10就會(huì)被舍棄,左側(cè)會(huì)空出兩個(gè)空位,空位用符號(hào)位補(bǔ)齊,前面說過正數(shù)的符號(hào)位是0,也就是用0補(bǔ)齊,得到的結(jié)果如下:
2:00000000 00000000 00000000 00000010
得到的十進(jìn)制結(jié)果是2,結(jié)論就是10>>2=2。
以-10>>2為例。
-10:11111111 11111111 11111111 11110110
同樣是右移,溢出的部分舍棄,空位的部分用符號(hào)位補(bǔ)齊,得到的結(jié)果如下:
11111111 11111111 11111111 11111101
減一操作后結(jié)果:
11111111 11111111 11111111 11111100
取反碼后的結(jié)果:
00000000 00000000 00000000 00000011
對(duì)應(yīng)的十進(jìn)制結(jié)果是3,因?yàn)槭秦?fù)數(shù),那么結(jié)果就是-3。結(jié)論是-10>>2。
整體的總結(jié)來看,正數(shù)的左移和右移沒有什么問題,但是負(fù)數(shù)存在一個(gè)補(bǔ)碼的問題,比較麻煩。補(bǔ)碼記住一個(gè)口訣就可以,移位、補(bǔ)碼、減一、取反碼,得到正數(shù)結(jié)果加個(gè)負(fù)號(hào)。這樣就可以得到正確結(jié)果。
Java中沒有無符號(hào)左移的說法,這里只說右移。同樣也是分正數(shù)和負(fù)數(shù)來講。
以10>>>2為例。
10:00000000 00000000 00000000 00001010
右移后,左側(cè)空出的位置用0補(bǔ)齊,但是這里需要注意的是這個(gè)0并不是指符號(hào)位,只是一個(gè)普通的補(bǔ)位。得到的結(jié)果如下:
2:00000000 00000000 00000000 00000010
得到的十進(jìn)制結(jié)果是2,結(jié)論就是10>>2=2。這個(gè)和有符號(hào)位移是得到相同的結(jié)果。
以-10>>>2為例。
-10:11111111 11111111 11111111 11110110
右移后,左側(cè)空位用0補(bǔ),注意不是用1補(bǔ),后面說原因。
00111111 11111111 11111111 11111101
這個(gè)結(jié)果就很大了,結(jié)果是1073741821,負(fù)數(shù)變成了這么大的負(fù)數(shù),不要懷疑自己的眼神,這個(gè)結(jié)果是正確的。
所謂的無符號(hào)右移,就是將原有的二進(jìn)制值直接右移得到結(jié)果,不論是負(fù)數(shù)還是正數(shù),沒有補(bǔ)碼的操作,補(bǔ)位都統(tǒng)一使用0,而不是對(duì)應(yīng)的符號(hào)位1或0。
到這里N種基本的位運(yùn)算已經(jīng)說明結(jié)束,接下來看幾個(gè)例子,和在代碼中常用的一些技巧。
// true表示為奇數(shù),false表示為偶數(shù)
public boolean checkNum(int num){
return (num&1) == 1;
}
1的二進(jìn)制是00000000 00000000 00000000 00000001,&運(yùn)算的規(guī)則是只有都是1,結(jié)果才是1,很明顯,不管是什么數(shù),和1進(jìn)行&運(yùn)算,前31位都是0,只有最后一位可能得出不同的結(jié)果。偶數(shù)最后一位肯定是0,奇數(shù)肯定是1,那么結(jié)果就顯而易見了。
// 傳入的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);
}
/*
輸出結(jié)果:
a=5
b=3
*/
這個(gè)靈活的用到了異或來實(shí)現(xiàn)。下面做個(gè)簡單的說明。
a = 3:00000011
b = 5:00000101
a異或b得到的結(jié)果是:00000110,這個(gè)結(jié)果賦值給a;
b異或a得到的結(jié)果是:00000011,這個(gè)結(jié)果賦值給b;
a異或b得到的結(jié)果是:00000101,這個(gè)結(jié)果賦值給a;
所以最終得到的結(jié)果是:
a = 5:00000101
b = 3:00000011
public int getAbs(int n){
return (n ^ (n >> 31)) - (n >> 31);
}
具體不說,可以自己嘗試著寫一下。(因?yàn)樯婕暗秸龜?shù)、負(fù)數(shù)的舉例,然后負(fù)數(shù)又有補(bǔ)碼操作,要寫的內(nèi)容太多,本博主就傲嬌一下,不寫啦!)
其實(shí)這里的例子很多,就不都去說了,有興趣的可以度娘一下相關(guān)的博客,有很多文章。
涉及到公司的東西,這里不會(huì)寫的很具體,給思路。
現(xiàn)在有一個(gè)訂單,訂單可以進(jìn)行很多操作,如:創(chuàng)建、修改、退單、接單、終止、派單、提交完成等操作。同時(shí)訂單也有很多狀態(tài),如:待接單、進(jìn)行中、已終止、已完成等狀態(tài)?,F(xiàn)在的場景就是不同的狀態(tài)對(duì)應(yīng)的操作是不一樣的,前端需要根據(jù)不同的狀態(tài)顯示不同的操作控件。
思路一:
最簡單也是最麻煩的,用N個(gè)flag字段表示可進(jìn)行的操作,返回?cái)?shù)據(jù)的時(shí)候?qū)γ總€(gè)flag字段賦值true、false,表示是否能夠進(jìn)行此操作。但是這樣存在一個(gè)巨大的問題,當(dāng)后期有一個(gè)操作去掉了或者添加了幾種新的操作,這個(gè)時(shí)候就涉及到字段的刪除和新增,改動(dòng)較大,可維護(hù)性差,不夠靈活。
思路二:(推薦)
使用此二進(jìn)制的方式表示。實(shí)現(xiàn)如下:
第一位:0,1表示是否可以修改
第二位:0,1表示是否可以接單
第三位:0,1表示是否可以終止
返回給前端只要一個(gè)字段tags,如對(duì)應(yīng)的值是:010,那就表示不可以修改、可以接單、不可以終止。
如果現(xiàn)在終止操作不要了,那么這個(gè)二進(jìn)制位始終給0就可以。如果新增了一個(gè)提交操作,那就讓第四位表示是否可以提交。這樣不需要添加字段,只要在原字段加一位就OK。
另外還有其他很多應(yīng)用場景。