什麼?你還不會用位運算來操作狀態?

Airsaid發表於2019-08-09

回顧

首先來回顧一下位運算,什麼是位運算呢?

位運算就是直接對整數在記憶體中的二進位制位進行操作。

在 Java 語言中,位運算有如下這些:

  • 左移(<<)。

  • 右移(>>)。

  • 無符號右移(>>>)。

  • 與(&)。

  • 或(|)。

  • 非(~)。

  • 異或(^)。

在本篇文章中,我們所需要用到的有如下幾個(其他的後續文章再講):

  • &(與運算):只有當兩方都為 true 時,結果才是 true,否則為 false。

  • |(或運算):只要當一方為 true 時,結果就是 true,否則為 false。

  • ^(異或運算):只要兩方不同,結果就是 true,否則為 false。

以 true、false 為例:


true & true  = true

true & false = false



true | false = true;

false | false = false;



true ^ true = false;

true ^ false = true;

複製程式碼

以數字運算為例:


6 & 4 = ?

6 | 4  = ?

6 ^ 4 = ?

複製程式碼

當以數字運算時,我們首先需要知道這些數字的二進位制,假設 6 是 int 型別,那麼其二進位制如下:


00000000 00000000 00000000 00000110

複製程式碼

在 Java 中,int 佔了 4 個位元組(Byte),一個位元組呢又等於 8 個 Bit 位。所以 int 型別的二進位制表現形式如上。

在這裡為方便講解,直接取後 8 位:00000110。

4 的二進位制碼如下:


00000100

複製程式碼

在二進位制碼中,1 為 true,0 為 false,根據這個,我們再來看看 6 & 4 的運算過程:


    00000110

    00000100

    -----------

    00000100

複製程式碼

對每位的數進行運算後,結果為 4。

再來看看 | 運算:


6 | 4 = ?

複製程式碼

6 和 4 的二進位制上面已經說了:


    00000110

    00000100

    -----------

    00000110


複製程式碼

可以發現最後的結果是 6。

最後再來看看 ^ 運算:


6 ^ 4 = ?

複製程式碼

    00000110

    00000100

    -----------

    00000010

複製程式碼

結果是 2。

應用

通過上面的例子,我們已經回顧了 & 、 | 以及 ^ 運算。現在來將它應用到實際的應用中。

假如我們現在要定義一個人的模型,這個人可能會包含有多種性格,比如說什麼樂觀型、內向型啦...

要是想要知道他包含了哪種性格,那麼我們該如何判斷呢?

可能在第一時間會想到:


if(這個人是樂觀性){

    ....

}else if(這個人是內向型){

    ...

}

複製程式碼

那麼如果有很多種性格呢?一堆判斷寫起來真的是很要命..

下面就來介紹一種更簡單的方式。首先來定義一組數:


public static final int STATUS_NORMAL     = 0;
public static final int STATUS_OPTIMISTIC = 1;
public static final int STATUS_OPEN       = 2;
public static final int STATUS_CLOSE      = 4;
複製程式碼

把它們轉換為二進位制:


0000 0000 0000 0000
0000 0000 0000 0001
0000 0000 0000 0010
0000 0000 0000 0100
複製程式碼

發現其中二進位制的規律沒有?都是 2 的次冪,並且二進位制都只有一個為 1 位,其他都是 0 !

然後再來定義一個變數,用於儲存狀態(預設值是 0):


private static int mStatus = STATUS_NORMAL;
複製程式碼

當我們要儲存狀態時,直接用 | 運算即可:


mStatus |= STATUS_OPTIMISTIC;
複製程式碼

儲存的運算過程如下:


    00000000

                    執行 | 運算(只要有 1 則為 1)

    00000001

    -----------

    00000001 = 1

複製程式碼

相當於就把這個 1 儲存到 0 的二進位制當中了。

那麼如果要判斷 mStatus 中是否有某個狀態呢?使用 & 運算:


System.out.println((mStatus & STATUS_OPTIMISTIC) != 0);// true,代表有它
複製程式碼

計算過程如下:


    00000001

                   執行 & 運算(都為 1 才為 1)

    00000001

    -----------

    00000001 = 1

複製程式碼

再來判斷一個不存在的狀態 mStatus & STATUS_OPEN


System.out.println((mStatus & STATUS_OPEN) != 0);// false,代表沒有它
複製程式碼

計算過程如下:


    00000001

    00000010

    -----------

    00000000 = 0

複製程式碼

可以發現,因為 STATUS_OPEN 這個狀態的二進位制位,1 的位置處,mStatus 的二進位制並沒有對於的 1,而又因為其他位都是 0,導致全部歸 0,計算出來的結果自然也就是 0 了。

這也就是為什麼定義狀態的數字中,是 1、2、4 這幾位數了,因為他們的特定就是二進位制只有一個為 1 的位,其他位都是 0,並同其他數位 1 的位不衝突。

如果換成其他的數,就會有問題了。比如說 3:


mStatus |= 3

複製程式碼

計算過程:


    00000000

    00000011

    -----------

    00000011 = 3

複製程式碼

運算完畢,這時候 mStatus 中已經儲存了 3 這個值了,我們再來判斷下是否存在 2:


System.out.println((mStatus & 2) != 0);// true,代表有它,但是其實是沒有的
複製程式碼

    00000011

    00000010

    -----------

    00000010 = 2

複製程式碼

結果是 true,但是其實我們只儲存了 3 到 mStatus 中,結果肯定是錯誤的。

所以我們在定義的時候,一定不要手滑定義錯了數字。

儲存和判斷已經說了,那麼如何取出呢?這時候就要用到 ^ 運算了。

假如現在 mStatus 中已經儲存了 STATUS_OPTIMISTIC 狀態了,要把它給取出來,這樣寫即可:


mStatus ^= STATUS_OPTIMISTIC

複製程式碼

其中的運算過程:


    00000001

                    執行 ^ 運算,兩邊不相同,則為 true

    00000001

    -----------

    00000000

複製程式碼

可以看到狀態又回到了最初沒有儲存 STATUS_OPTIMISTIC 狀態的時候了。

最後再來看一個取出的例子,這次是先儲存兩個狀態,然後再取出其中一個:


mStatus |= STATUS_OPTIMISTIC

mStatus |= STATUS_OPEN

複製程式碼

儲存完後,mStatus 的二進位制為:


00000011

複製程式碼

再來取出 STATUS_OPEN 這個狀態:


mStatus ^= STATUS_OPEN

複製程式碼

運算過程:


00000011

00000010

-----------

00000001

複製程式碼

mStatus 現在就只有 STATUS_OPTIMISTIC 的狀態了。

總結

通過 |、^、& 運算,我們可以很方便快捷的對狀態值進行操作。當然,位運算的應用不僅限於狀態值,知道了其中的二進位制運算原理後,還有更多的其他應用場景,等著你去發現。

相關文章