簡介:
大家在閱讀源碼的時候經常會看到一些比如下面這樣特別難理解的代碼。
cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
order = ((order) >> (INDEX_OFFSET -1) + 1) << INDEX_OFFSET;
類似與這種“高大上”的代碼在各類大神寫的代碼或源碼中隨處可見。
這種代碼是什麼意思?為什麼要這麼寫?這麼寫的好處?怎麼才能寫出這樣的代碼?我們就帶著這些疑問來看下面的文章吧。
邏輯運算子:
邏輯運算子主要用來計算真假值(boolean),通常用在條件判斷語句中。
邏輯運算是我們平時用的最多的,主要有&&,||,&,|。相信大家都熟悉,所以這裡只簡單介紹一下&&和&的區別:
對於&&和||,如果左邊判斷通過就不會判斷右邊了。而&和|則是兩邊等式都需要計算。
(所以這裡有個技巧,如果不需要兩邊都判斷的話則用&&或||並且可以把需要用時多的計算條件放在右邊,用時少的放在左邊)
位元運算符:
位元運算符主要對位元(int)做操作。
包括:與(&),或(|),非(~),異或(^),左移(<<),右移(>>>),正負右移(>>)。
與(&):兩邊都是1(true),結果才為1(true),否則結果為0(false)
例:1 & 2 = 0001 & 0010 = 0000 = 0
或(|):兩邊有一個為1(true),結果就為1(true)。兩邊都是0(false),結果才是0(false)
例:1 | 3 = 0001 | 0011 = 0011 = 3
非(~):如果位為0(false),結果是1(true),如果位為1(true),結果是0(false)
例:~ 4 = ~0100 = 1011 = 11
異或(^):兩個運算元的位中,相同則為0(false),不同則為1(true)
例:1 ^ 4 = 0001 ^ 0100 = 0101 = 5
左移(<<):向左移動運算子右邊指定的位元,並且在低位補零。或相當於將(左邊的值)乘上2 的(右邊值) 次方
例:1 << 4 = 0001向左移動4位 = 10000 = 16 或者 = 1 * 2^4 = 16
右移(>>>):向右移動運算子右邊指定的位元,並且在高位補零。或相當於將(左邊的值)除以2 的(右邊值) 次方
例:4 >>> 2 = 0100向右移動2位 = 0001 = 1 或者 = 4 / 2^2 = 1
正負右移 (>>):向右移動運算子右邊指定的位元。如果是正數,在高位補零,如果是負數,則在高位補1
例:
4 >> 2 = 0100向右移動2位後高位補0 = 0001 = 1
-4 >> 2 = -0100向右移動2位後高位補1 = 1011 向右移動兩位後高位補1 = 1110 = 14
在程式中使用位元運算:
首先我們要有一個概念,使用二進位的目的就是要用1和0當做標誌位,通常把1當成true,0當成false。這樣就可以在一個變數中攜帶多個標誌位資訊。
這裡舉個例子,大家看後就明白了。
需求是這樣的,有4個許可權判斷,分別是A,B,C,D。我們要根據不同的許可權組合來進行不同的操作。
public static final int PASS_BY_A = 1;//0001public static final int PASS_BY_B = 2;//0010public static final int PASS_BY_C = 4;//0100public static final int PASS_BY_D = 8;//1000public static final int PASS_BY_ABCD = PASS_BY_A|PASS_BY_B|PASS_BY_C|PASS_BY_D; //1111/** * @param args */public static void main(String[] args) {boolean isA = false;boolean isB = false;boolean isC = false;boolean isD = false;int isPass = -1;// 擷取許可權然後為各個許可權變數賦值,這裡忽略initParameter();// 使用傳統的boolean去做判斷,是否滿足ABCD四個許可權if (isA & isB & isC & isD) {}// 使用二進位做判斷,是否滿足ABCD四個許可權if ((isPass & PASS_BY_ABCD) == 0) {}}通過對比可以看出,使用二進位做判斷,變數的使用量,效率還有代碼簡潔程度大大提高。這也是在各種各樣的源碼裡二進位和位元運算被經常使用的原因。
位元運算提示總結:
最後我總結出了一些我在使用二進位和位元運算時發現的一些小技巧。
a | b:合并ab中的1。
例1:a為1100(12),b為0001(1),a|b=1101(13)。
例2:a=0001(1)。這時候想讓a=1111(15)。最簡單的做法就是找個b=1110(14)。a | = b (a = a | b)
a & b:從a中去掉b中的1。
例1:a為1010(10),b為1000(8); a & b = 0010 (2)。如果 a & b = 0000(0),這就說明a和b的標誌位完全相同,如果 a & b != 0 表示ab不同。經常使用它來做判斷
例2:a=1111(15)。這時候想讓a=1001(9)。最簡單的做法就是找個b=0110(6)。a & = b (a = a & b)
a ^ b:找出ab中的不同/相同(1為不同,0為相同)。
例1:比如a為1100(12),b為0111(7)。a ^ b = 1011(11)
a << b 和 a >>> b:左移和右移通常都是為二進位賦值,和改變值
例1:a=0001(1)。a<<1=0010(2), a<<2=0100(4), a<<3=1000(8),配合for迴圈可以很快的為二進位賦值。
例2:a=1100(12), INDEX_OFFSET = 0001(1)。 a >>>= INDEX_OFFSET = 0110(6), a >>>= INDEX_OFFSET = 0011(3)。
而且大家在程式中做2倍數乘除法的時候最好是使用左移和右移操作,這樣效率會提高很多。
最後列出4*4的二級制數集合
| 0=0000 |
1=0001 |
2=0010 |
3=0011 |
| 4=0100 |
5=0101 |
6=0110 |
7=0111 |
| 8=1000 |
9=1001 |
10=1010 |
11=1011 |
| 12=1100 |
13=1101 |
14=1110 |
15=1111 |
標註顏色的2組為比較常用的。可以的話大家可以背下來幾組,免得現用現算,這樣編程效率就會大大增加。
一般在做程式的時候4*4個標誌位基本夠用了。如果不夠可以5*5,6*6等等。使用標誌位判斷越多的時候二進位的優勢就越明顯。
總結&感受:
我在平時做項目,做程式的時候很少有機會使用演算法。我覺得使用二進位和位元運算就可以算做為一種簡單的演算法,因為使用演算法的目的就是為了給程式“減壓”,但是相應會給程式員“增壓”。為了不做Code Monkey或者Code Farmer,我們應該主動的找機會給自己“增壓”。在平時有機會一定要儘可能多的使用演算法,多學習源碼和大神的編程風格,提高自己的水平,寫出高效、優雅、藝術的代碼!
相信大家看完這篇文章後,會對二進位在程式中做標誌位的用法有所瞭解,最起碼在看源碼的時候不會一頭霧水。另外我目前只發現了二進位在程式中做標誌位的作用,也許還有其他的應用,大家知道的話希望可以分享一下。